﻿using System.Windows;
using System.Windows.Input;
using System.IO;
using System.Collections.Generic;
using Microsoft.Win32;
using ICSharpCode.AvalonEdit.Highlighting;
using System.Text;
using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Documents;
using ICSharpCode.AvalonEdit.Utils;
using System.Windows.Media.Imaging;
using System.Xml;
using System.Windows.Markup;
using MahApps.Metro.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using ICSharpCode.AvalonEdit.Document;
using System.Media;
using System.Text.RegularExpressions;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        /// <summary>
        /// Html Help Workshop安装路径。
        /// </summary>
        private string hhwInstalledPath = Globals.hhwInstalledPath;
        /// <summary>
        /// Html Help Workshop安装路径。
        /// </summary>
        public string HHWInstalledPath { get { return this.hhwInstalledPath; } }

        public MainWindow()
        {
            InitializeComponent();

            //提供托盘图标
            CreateNoticeIcon();
            //保证窗体显示在上方。
            wsl = WindowState;

            helpFrame.Source = new Uri(Globals.PathOfWorkspace + "Help~.html");//不能加这个前缀"file:///" +

            //载入之前指定的各个历史工作区目录
            if (File.Exists(Globals.PathOfHistoryWorkspaceFileFullName))
            {
                string[] workapaces = File.ReadAllLines(Globals.PathOfHistoryWorkspaceFileFullName);
                if (workapaces.Length > 0)
                {
                    for (int i = workapaces.Length - 1; i >= 0; i--)
                    {
                        if (string.IsNullOrEmpty(workapaces[i]) == false)
                        {
                            var rw = new RecentDirectoryListBoxItem(workapaces[i]);
                            rw.MouseDoubleClick += rw_MouseDoubleClick;
                            lbxHistoryWorkspaces.Items.Add(rw);
                        }
                    }
                }
            }

            RefreshWorkspaceHistoryList();

            //载入之前指定的各个历史导出目录
            if (File.Exists(Globals.PathOfHistoryOutputFileFullName))
            {
                string[] outports = File.ReadAllLines(Globals.PathOfHistoryOutputFileFullName);
                if (outports.Length > 0)
                {
                    for (int i = outports.Length - 1; i >= 0; i--)
                    {
                        if (string.IsNullOrEmpty(outports[i]) == false)
                        {
                            var row = new RecentDirectoryListBoxItem(outports[i]);
                            row.MouseDoubleClick += row_MouseDoubleClick;
                            lbxHistoryOutport.Items.Add(row);
                        }
                    }
                }
            }

            RefreshOutputHistoryList();

            this.configManager = new XmlConfigManager(Globals.PathOfUserFolder + "config.xml");

            //载入指定的工作区目录
            string workspace = this.configManager.Get("Workspace");
            if (string.IsNullOrEmpty(workspace) == false && Directory.Exists(workspace))
            {
                if (workspace.EndsWith("\\") == false) workspace += "\\";
                if (Directory.Exists(workspace))
                {
                    Globals.PathOfWorkspace = workspace;
                }

                //检查必须的文件是否存在。
                CopyCssAndResourceFiles(workspace);
            }

            if (Directory.Exists(Globals.PathOfWorkspace) == false)
            {
                try
                {
                    Directory.CreateDirectory(Globals.PathOfWorkspace);
                    CopyCssAndResourceFiles(Globals.PathOfWorkspace);
                }
                catch (System.Exception ex)
                {
                    LMessageBox.Show("　　未能在创建工作区目录。这可能是因为Windows系统权限造成的。一般来说，不应将工作区指定在系统目录下。异常消息如下：\r\n" + ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            else
            {
                if (Globals.PathOfWorkspace.ToLower() != Globals.DefaultWorkspacePath.ToLower())
                {
                    //检查必须的文件是否存在。
                    CopyCssAndResourceFiles(Globals.PathOfWorkspace);
                }
            }

            #region ExplorerBrowser初始化

            //为提高效率，且考虑到不需要多大的横向距离，本程序不使用“保存文件”“打开文件”这两个对话框。
            tbxPathOfWorkspace.Text = Globals.PathOfWorkspace;
            //tbxPathOfAppDocument.TextDecorations.Add(new TextDecoration() { Location = TextDecorationLocation.Underline });

            //explorerBrowser.ExplorerBrowserControl.Navigate(pathOfAppDocumentsFolder);

            //explorerBrowser.ExplorerBrowserControl.NavigationLog.CurrentLocation.ParsingName//取当前目录，需要判断是否合法。

            //这两个事件都可以有效监测当前选定的目录是否变化
            //explorerBrowser.ExplorerBrowserControl.NavigationComplete += ExplorerBrowserControl_NavigationComplete;
            //explorerBrowser.ExplorerBrowserControl.NavigationLog.NavigationLogChanged += NavigationLog_NavigationLogChanged;

            #endregion

            #region 快捷键事件处理

            //注意，某些快捷键是全局的，因此可以放在主窗口事件中处理；
            //例如增加字号、减小字号、保存文件、全部保存、新建文件、打开文件、关闭窗口（不需要处理）、生成HTML、生成填空文本、帮助文档……
            //而另一些快捷键必须放到TabControl的事件中处理。
            //这些命令使用的快捷键与Explorer使用的快捷键会产生冲突，此时必须在TabControl中实现。
            //包括：Ctrl+X,Ctrl+C,Ctrl+V,Ctrl+A,Ctrl+Z,Ctrl+Y,Del

            //*** 不清楚是不是会产生冲突的快捷键，为防止误操作，一律在主窗口的键盘事件中进行处理

            this.PreviewKeyDown += MainWindow_PreviewKeyDown;
            mainTabControl.PreviewKeyDown += mainTabControl_PreviewKeyDown;

            #endregion

            this.Closing += MainWindow_Closing;

            this.workspaceManager = new WorkspaceManager(this);
            this.workspaceManager.Refresh(null);

            mainTabControl.SelectionChanged += mainTabControl_SelectionChanged;

            this.Loaded += MainWindow_Loaded;

            //载入字体列表
            InitializeFontFamilyList();

            //载入默认字体
            this.defaultFontFamily = mainTabControl.FontFamily;
            var defFontFamilyEnName = this.configManager.Get("FontFamily");
            foreach (var item in fontFamilyList.Items)
            {
                FontFamilyListItem fli = item as FontFamilyListItem;
                if (fli == null) continue;

                string enName;
                if (fli.FontFamily.FamilyNames.TryGetValue(System.Windows.Markup.XmlLanguage.GetLanguage("en-us"), out enName))
                {
                    if (defFontFamilyEnName == enName)
                    {
                        this.mainTabControl.FontFamily = fli.FontFamily;
                        this.fontFamilyList.SelectedItem = fli;
                        break;
                    }
                }
            }

            this.fontFamilyList.SelectionChanged += fontFamilyList_SelectionChanged;

            //载入格式化动作何时执行的设置
            var format = this.configManager.Get("AutoFormat");
            if (string.IsNullOrEmpty(format) == false && (format.ToLower() == "true"))
                miFormatBeforeSave.IsChecked = true;
            else
                miFormatBeforeSave.IsChecked = false;
        }

        #region 字体相关

        private FontFamily defaultFontFamily;

        public FontFamily DefaultFontFamily
        {
            get { return defaultFontFamily; }
            set { defaultFontFamily = value; }
        }

        private bool _familyListValid;
        private ICollection<FontFamily> _familyCollection;

        public ICollection<FontFamily> FontFamilyCollection
        {
            get
            {
                return (_familyCollection == null) ? Fonts.SystemFontFamilies : _familyCollection;
            }

            set
            {
                if (value != _familyCollection)
                {
                    _familyCollection = value;
                    InvalidateFontFamilyList();
                }
            }
        }

        private void InvalidateFontFamilyList()
        {
            if (_familyListValid)
            {
                fontFamilyList.Items.Clear();
                _familyListValid = false;
            }
        }

        private void InitializeFontFamilyList()
        {
            ICollection<FontFamily> familyCollection = FontFamilyCollection;
            if (familyCollection != null)
            {
                FontFamilyListItem[] items = new FontFamilyListItem[familyCollection.Count];

                int i = 0;

                foreach (FontFamily family in familyCollection)
                {
                    items[i++] = new FontFamilyListItem(family);
                }

                Array.Sort<FontFamilyListItem>(items);

                foreach (FontFamilyListItem item in items)
                {
                    fontFamilyList.Items.Add(item);
                }
            }
        }

        #endregion

        void rw_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            ChangeWorkspace((sender as RecentDirectoryListBoxItem).DirectoryPath);
        }

        private static void CopyCssAndResourceFiles(string workspace)
        {
            if (string.IsNullOrEmpty(workspace)) return;

            if (workspace.EndsWith("\\") == false) workspace += "\\";

            var destWorkspace = workspace;
            if (destWorkspace == Globals.DefaultWorkspacePath) return;//不需要复制。

            var destImageFolder = workspace + "Images~\\";
            if (Directory.Exists(destImageFolder) == false)
            {
                Directory.CreateDirectory(destImageFolder);
            }

            try
            {
                var destDarkHeaderImage = destImageFolder + "header_icon_dark.png";
                //if (File.Exists(destDarkHeaderImage) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "Images~\\header_icon_dark.png", destDarkHeaderImage, true);
                //}

                var destLightHeaderImage = destImageFolder + "header_icon_light.png";
                //if (File.Exists(destLightHeaderImage) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "Images~\\header_icon_light.png", destLightHeaderImage, true);
                //}

                var destDarkCommentImage = destImageFolder + "comment_dark.png";
                //if (File.Exists(destDarkCommentImage) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "Images~\\comment_dark.png", destDarkCommentImage, true);
                //}

                var destLightCommentImage = destImageFolder + "comment_light.png";
                //if (File.Exists(destLightCommentImage) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "Images~\\comment_light.png", destLightCommentImage, true);
                //}

                if (Directory.Exists(workspace) == false)
                {
                    Directory.CreateDirectory(workspace);
                }

                var destJQuery = workspace + "jquery-1.7.0.min.js";
                if (File.Exists(destJQuery) == false)
                {
                    File.Copy(Globals.DefaultWorkspacePath + "jquery-1.7.0.min.js", destJQuery);
                }

                var destJSON = workspace + "json2.js";
                if (File.Exists(destJSON) == false)
                {
                    File.Copy(Globals.DefaultWorkspacePath + "json2.js", destJSON);
                }

                var destLessonDarkCSS = workspace + "lesson_dark.css";
                //if (File.Exists(destLessonDarkCSS) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "lesson_dark.css", destLessonDarkCSS, true);
                //}

                var destLessonLightCSS = workspace + "lesson_light.css";
                //if (File.Exists(destLessonLightCSS) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "lesson_light.css", destLessonLightCSS, true);
                //}

                var destHelpHtml = workspace + "Help~.html";
                //if (File.Exists(destHelpHtml) == false)
                //{
                File.Copy(Globals.DefaultWorkspacePath + "Help~.html", destHelpHtml, true);
                //}

                var destJQueryPrint = workspace + "jquery.jqprint-0.3.js";
                if (File.Exists(destJQueryPrint) == false)
                {
                    File.Copy(Globals.DefaultWorkspacePath + "jquery.jqprint-0.3.js", destJQueryPrint);
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message);
            }
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //启动时全屏
            var startFullScreen = this.configManager.Get("StartFullScreen");
            if (string.IsNullOrEmpty(startFullScreen) == false)
            {
                bool sf;
                if (bool.TryParse(startFullScreen, out sf))
                {
                    this.StartFullScreen = sf;

                    if (sf) SwitchFullScreen(true);
                }
            }
            miStartFullScreen.IsChecked = this.StartFullScreen;

            helpFrame.Source = new Uri(Globals.DefaultWorkspacePath + "Help~.html");//不能加此前缀"file:///" + 

            var s = configManager.Get("color");
            if (string.IsNullOrEmpty(s) == false)
            {
                foreach (var i in cmbColor.Items)
                {
                    ComboBoxItem cbi = i as ComboBoxItem;
                    if (cbi == null) continue;

                    if (cbi.Tag.ToString() == s)
                    {
                        cbi.IsSelected = true;
                        break;
                    }
                }
            }
            cmbColor_SelectionChanged(sender, null);

            //读取Html Help Workshop.exe的路径。
            if (File.Exists(Globals.hhwInstalledPath) == false)
            {
                //为什么要这样做呢？
                //这是因为版权问题，不便集成Html Help Workshop到我的安装包中。
                //但问题在于，从网上下载的各个版本（哪怕是微软官方网站下载的英文版），在独立状态下都很容易出错。
                //所以还是集成了。这样，原先分离的做法就不想作用了。
                //如果将来还可以分离，这段代码就又有用了。所以未删除。
                var hhwInstalledPath = this.configManager.Get("HHWInstalledPath");
                if (File.Exists(hhwInstalledPath))
                {
                    this.hhwInstalledPath = hhwInstalledPath;
                }
            }

            var isExamEnabledText = this.configManager.Get("IsExamEnabled");
            if (string.IsNullOrEmpty(isExamEnabledText) == false)
            {
                bool r;
                if (bool.TryParse(isExamEnabledText, out r))
                {
                    this.IsExamEnabled = r;
                }
            }
            else
            {
                this.IsExamEnabled = true;//默认打开
            }
            miIsExamEnabled.IsChecked = this.IsExamEnabled; ;

            var rememberOpenedFilesConfig = this.configManager.Get("RememberOpenedFiles");
            if (string.IsNullOrEmpty(rememberOpenedFilesConfig) == false)
            {
                bool r;
                if (bool.TryParse(rememberOpenedFilesConfig, out r))
                {
                    this.RememberOpenedFiles = r;
                }
            }
            miRememberOpenedFiles.IsChecked = this.RememberOpenedFiles;
            LoadRememberOpenedFiles();//会自动判断要不要载入。

            var selectCellFirst = this.configManager.Get("SelectCellFirst");
            if (string.IsNullOrEmpty(selectCellFirst) == false)
            {
                bool se;
                if (bool.TryParse(selectCellFirst, out se))
                {
                    this.SelectCellFirst = se;
                }
            }
            miSelectCellFirst.IsChecked = this.SelectCellFirst;

            var collapseH1WhenCompile = this.configManager.Get("CollapseH1WhenCompile");
            if (string.IsNullOrEmpty(collapseH1WhenCompile) == false)
            {
                bool ct;
                if (bool.TryParse(collapseH1WhenCompile, out ct))
                {
                    this.CollapseH1WhenCompile = ct;
                }
            }
            miCollapseH1TitlesWhenCompile.IsChecked = this.CollapseH1WhenCompile;

            //miFillblankMode.IsChecked = !miFillblankMode.IsChecked;
            //this.ConfigManager.Set("CompileCodeToFillBlank", miFillblankMode.IsChecked.ToString());
            var compileCodeToFillBlank = this.configManager.Get("CompileCodeToFillBlank");
            if (string.IsNullOrEmpty(compileCodeToFillBlank) == false)
            {
                bool cctfb;
                if (bool.TryParse(compileCodeToFillBlank, out cctfb))
                {
                    this.CompileCodeToFillBlank = cctfb;
                }
            }
            miFillblankMode.IsChecked = this.CompileCodeToFillBlank;

            var hideChoiceQuestionAnswer = this.configManager.Get("HideChoiceQuestionAnswer");
            if (string.IsNullOrEmpty(hideChoiceQuestionAnswer) == false)
            {
                bool hcqa;
                if (bool.TryParse(hideChoiceQuestionAnswer, out hcqa))
                {
                    this.HideChoiceQuestionAnswer = hcqa;
                }
            }
            miHideChoiceQuestionAnswer.IsChecked = this.HideChoiceQuestionAnswer;

            //TextAutoWrap，文本自动折行
            var textAutoWrap = this.configManager.Get("TextAutoWrap");
            if (string.IsNullOrEmpty(textAutoWrap) == false)
            {
                bool txtAutoWrap;
                if (bool.TryParse(textAutoWrap, out txtAutoWrap))
                {
                    this.textAutoWrap = txtAutoWrap;//注意，不能使用属性，会引起无限递归
                }
            }
            miTextAutoWrap.IsChecked = this.TextAutoWrap;

            var isCloseToIconText = this.ConfigManager.Get("CloseToIcon");
            if (string.IsNullOrWhiteSpace(isCloseToIconText) == false)
            {
                this.isCloseToIcon = bool.Parse(isCloseToIconText);
            }
            miCloseToIcon.IsChecked = this.isCloseToIcon;

            var encoding = this.ConfigManager.Get("Encoding");
            if (string.IsNullOrEmpty(encoding) == false)
            {
                RefreshEncoding(encoding);
            }
            else
            {
                RefreshEncoding("utf-8");
            }

            var searchArea = this.ConfigManager.Get("SearchRange");
            if (string.IsNullOrWhiteSpace(searchArea) == false)
            {
                foreach (var item in cmbSearchArea.Items)
                {
                    var ci = item as ComboBoxItem;
                    if (ci == null) continue;

                    if ((ci.Tag as string) == searchArea)
                    {
                        cmbSearchArea.SelectedItem = ci;
                    }
                }
            }

            var windowStateText = this.ConfigManager.Get("WindowState");
            if (string.IsNullOrWhiteSpace(windowStateText) == false)
            {
                switch (windowStateText)
                {
                    case "Maximized":
                        {
                            this.WindowState = WindowState.Maximized;
                            break;
                        }
                    default:
                        {
                            this.WindowState = WindowState.Normal;
                            break;
                        }
                }
            }

            var fontSizeText = this.ConfigManager.Get("FontSize");
            if (string.IsNullOrWhiteSpace(fontSizeText) == false)
            {
                double newFontSize;
                if (double.TryParse(fontSizeText, out newFontSize))
                {
                    this.mainTabControl.FontSize = newFontSize;
                    RefreshFontSize();
                }
            }

            var ignoreEncryptedFiles = this.ConfigManager.Get("IgnoreEncryptedFiles");
            if (string.IsNullOrWhiteSpace(ignoreEncryptedFiles) == false)
            {
                bool ignoreEncryptedFilesValue;
                if (bool.TryParse(ignoreEncryptedFiles, out ignoreEncryptedFilesValue))
                {
                    this.IgnoreEncryptedFiles = ignoreEncryptedFilesValue;
                }
            }
            miIgnoreEncryptedFile.IsCheckable = this.IgnoreEncryptedFiles;

            // 刷新查找、替换按钮的可用状态
            RefreshFindButtonsStatus();
        }

        public string ColorText
        {
            get
            {
                if (cmbColor.SelectedItem == null)
                {
                    cmbColor.SelectedIndex = 0;
                    return "light";//默认
                }

                return (cmbColor.SelectedItem as ComboBoxItem).Tag.ToString();
            }
        }

        /// <summary>
        /// 用于刷新树型框列表。
        /// </summary>
        private WorkspaceManager workspaceManager;

        internal WorkspaceManager WorkspaceManager
        {
            get
            {
                if (this.workspaceManager == null)
                    this.workspaceManager = new WorkspaceManager(this);
                return workspaceManager;
            }
        }

        private XmlConfigManager configManager;
        /// <summary>
        /// 用于保存配置的Xml配置管理器。
        /// </summary>
        public XmlConfigManager ConfigManager
        {
            get { return configManager; }
        }

        public void Undo()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.CanUndo)
            {
                eti.EditorBase.Undo();
            }
        }

        public void Redo()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.CanRedo)
            {
                eti.EditorBase.Redo();
            }
        }

        private void SaveDocment(bool formatBeforeSave)
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.IsModified == false) return;

            string oldFileFullPath = eti.FullFilePath;

            if (formatBeforeSave)
            {
                //eti.EditorBase.Text = eti.GetFormatedMarkdownText();
                eti.EditorBase.Format();
            }

            eti.SaveDocument();

            if (eti.FullFilePath != oldFileFullPath)
            {
                this.workspaceManager.Refresh(eti.FullFilePath);//刷新工作区文件列表。
            }
        }

        private void SaveAllDocuments()
        {
            List<MarkdownEditor> needSavingDocumentList = new List<MarkdownEditor>();
            foreach (var item in this.mainTabControl.Items)
            {
                MarkdownEditor eti = item as MarkdownEditor;
                if (eti != null && eti.IsModified)
                {
                    needSavingDocumentList.Add(eti);
                }
            }

            if (needSavingDocumentList.Count > 0)
            {
                string saveInfo;
                foreach (MarkdownEditor eti in needSavingDocumentList)
                {
                    if (miFormatBeforeSave.IsChecked)
                    {
                        eti.EditorBase.Text = eti.GetFormatedMarkdownText();
                    }
                    saveInfo = eti.SaveDocument();
                    if (saveInfo == "用户取消操作")
                    {
                        LMessageBox.Show("　　取消了操作。未能保存所有需要保存的文档。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    else if (saveInfo != string.Empty)
                    {
                        LMessageBox.Show("　　未能保存所有需要保存的文件。错误消息如下：\r\n" + saveInfo, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;//不关闭
                    }
                }
            }

            string shouldSelItemPath = null;
            WorkspaceTreeViewItem wi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wi != null)
            {
                shouldSelItemPath = wi.FullPath;
            }
            this.workspaceManager.Refresh(shouldSelItemPath);//刷新工作区文件列表。
        }

        void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (isForceExit == false)
            {
                if (this.IsCloseToIcon)
                {
                    this.Hide();
                    e.Cancel = true;
                    return;
                }
            }

            List<MarkdownEditor> needSavingDocumentList = new List<MarkdownEditor>();
            foreach (var item in this.mainTabControl.Items)
            {
                MarkdownEditor eti = item as MarkdownEditor;
                if (eti != null && eti.IsModified)
                {
                    needSavingDocumentList.Add(eti);
                }
            }

            if (needSavingDocumentList.Count > 0)
            {
                MessageBoxResult r = LMessageBox.Show(string.Format("　　有 {0} 个文档已被修改，要保存吗？", needSavingDocumentList.Count),
                    Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            string saveInfo;
                            foreach (MarkdownEditor eti in needSavingDocumentList)
                            {
                                saveInfo = eti.SaveDocument();
                                if (saveInfo == "用户取消操作")
                                {
                                    e.Cancel = true;
                                    isForceExit = false;//还原
                                    return;//不关闭
                                }
                                else if (saveInfo != string.Empty)
                                {
                                    LMessageBox.Show(saveInfo, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                                    e.Cancel = true;
                                    isForceExit = false;//还原
                                    return;//不关闭
                                }
                            }

                            break;
                        }
                    case MessageBoxResult.Cancel:
                        {
                            e.Cancel = true;
                            isForceExit = false;//还原
                            return;
                        }
                        //default://No,直接关闭窗口
                }
            }

            RememberOpenedFilesForWorkspace();

            this.ConfigManager.Set("SearchRange", (cmbSearchArea.SelectedItem as ComboBoxItem).Tag.ToString());
            this.ConfigManager.Set("WindowState", this.WindowState.ToString());
            //退出托盘图标
            notifyIcon?.Dispose();
        }

        private void RememberOpenedFilesForWorkspace()
        {
            //记录所有当前打开的文档的路径，下次自动打开
            if (this.RememberOpenedFiles)
            {
                //重置工作区配置文件管理器
                this.workspaceConfigManager = null;

                StringBuilder sb = new StringBuilder();
                foreach (var item in this.mainTabControl.Items)
                {
                    MarkdownEditor me = item as MarkdownEditor;
                    if (me == null || File.Exists(me.FullFilePath) == false) continue;

                    sb.Append(me.FullFilePath);
                    sb.Append(";");
                }
                this.WorkspaceConfigManager.Set("OpenedFiles", sb.ToString());

                //var curEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
                //if (curEditor != null && File.Exists(curEditor.FullFilePath))
                //{
                //    this.configManager.Set("EditingFile", curEditor.FullFilePath);
                //}
                //else
                //{
                //    this.configManager.Set("EditingFile", "");
                //}

                if (this.mainTabControl.SelectedItem == null)
                {
                    this.WorkspaceConfigManager.Set("ActiveDocumentFullPath", "");
                }
                else
                {
                    var editor = this.mainTabControl.SelectedItem as MarkdownEditor;
                    if (editor == null)
                    {
                        this.WorkspaceConfigManager.Set("ActiveDocumentFullPath", "");
                    }
                    else
                    {
                        this.WorkspaceConfigManager.Set("ActiveDocumentFullPath", editor.FullFilePath);
                    }
                }
            }
        }

        void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            //处理不会在ExplorerBrowser和编辑器操作之间产生冲突的快捷键。
            //处理可能存在冲突，但是ExplorerBrowser不需要使用或需要屏蔽其功能的快捷键。

            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;
            switch (e.Key)
            {
                case Key.N:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            //NewDocument();
                            NewFile(false);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.T:
                    {
                        if (isCtrl)
                        {
                            if (isShift)//Ctrl+Shift+T
                            {
                                FormatAsTextTable();
                                e.Handled = true;//否则会插入一个字母t
                            }
                            else
                            {
                                if (isAlt == false)//Ctrl+T
                                {
                                    //NewDocument();
                                    NewFile(false);
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.S:
                    {
                        if (isCtrl && isAlt == false)
                        {
                            if (isShift)
                            {
                                SaveAllDocuments();
                                e.Handled = true;
                            }
                            else
                            {
                                SaveDocment(miFormatBeforeSave.IsChecked);
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.O:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            //OpenDocuments();
                            miOpenDocument_Click(sender, e);//代码逻辑不同，现在不再允许打开外部文件。2016年2月3日
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.OemPlus:
                    {
                        if (isCtrl && isAlt == false)
                        {
                            if (isShift)
                            {
                                FontSizeUp();
                                e.Handled = true;
                            }
                            else
                            {
                                var selEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
                                if (selEditor != null)
                                {
                                    selEditor.EditorBase.FindNextHeader();
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.OemMinus:
                    {
                        if (isCtrl && isAlt == false)
                        {
                            if (isShift)
                            {
                                FontSizeDown();
                                e.Handled = true;
                            }
                            else
                            {
                                var selEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
                                if (selEditor != null)
                                {
                                    selEditor.EditorBase.FindPreviewHeader();
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.D0:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            this.mainTabControl.FontSize = 16;//默认字号16。
                            RefreshFontSize();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F1:
                    {
                        if (!isCtrl && !isShift && !isAlt)
                        {
                            ShowHelp();
                        }
                        break;
                    }
                case Key.U:
                    {
                        if (isCtrl && !isAlt)
                        {
                            if (isShift)
                            {
                                WrapWithSTag();
                            }
                            else
                            {
                                WrapWithUTag();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F5:
                    {
                        CompileAndPreviewHtml();
                        break;
                    }
                case Key.F11:
                    {
                        if (!isCtrl && !isShift && !isAlt)
                        {
                            SwitchFullScreen(mainMenu.Visibility == Visibility.Visible);
                        }
                        break;
                    }
                case Key.F12:
                    {
                        if (!isCtrl && !isAlt)
                        {
                            if (isShift == false) { SwitchLeftToolBarToggle(); }
                            else SwitchRightToolBarToggle();
                        }
                        break;
                    }
                case Key.D:
                    {
                        if (isShift && isCtrl && !isAlt)
                        {
                            InsertDateText();
                        }
                        break;
                    }
                case Key.G:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            if (this.mainTabControl.SelectedItem == null) return;
                            var edit = this.mainTabControl.SelectedItem as MarkdownEditor;
                            if (edit == null) return;

                            var line = edit.EditorBase.Document.GetLineByOffset(edit.EditorBase.SelectionStart);
                            var lineText = edit.EditorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);
                            var startIndex = edit.EditorBase.SelectionStart - line.Offset;

                            var leftIndex = lineText.IndexOf("](") + 1;
                            var rightIndex = lineText.IndexOf(')', startIndex);
                            if (leftIndex <= 0 || rightIndex <= 1 || rightIndex <= leftIndex + 1) return;

                            var subText = lineText.Substring(leftIndex + 1, rightIndex - leftIndex - 1);
                            if (subText.ToLower().Contains(".html"))
                            {
                                edit.OpenLinkedMarkdownFile();
                            }
                            else
                            {
                                edit.PreviewLinkedImageFile();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.E:
                    {
                        if (isCtrl && !isAlt && !isShift)
                        {
                            miIsExamEnabled_Click(sender, e);
                        }
                        break;
                    }
                case Key.R:
                    {
                        if (isCtrl && !isAlt && !isShift)
                        {
                            miTextWrap_Click(sender, e);
                        }
                        break;
                    }
                case Key.F:
                    {
                        if (isCtrl)
                        {
                            if (isShift)
                            {
                                if (isAlt == false)
                                {
                                    //Ctrl+Shift+F
                                    Format();
                                }
                                e.Handled = true;
                            }
                            //else
                            //{
                            //如果把查找放在这里，会导致无法在html页面上查找。                                
                            //}
                            //else
                            //{
                            //    //Find();
                            //    //AvalonEdit的搜索框有时无法输入中文，所以只得放弃
                            //    e.Handled = true;//加上这句后，第一次打开搜索框就无法聚焦了。
                            //    在这里会导致无法查找帮助文档中的关键词。
                            //}
                        }
                        break;
                    }
                case Key.H:
                    {
                        // Ctrl+H
                        if (isCtrl && !isAlt && !isShift)
                        {
                            if (gdFindAndReplace.Visibility != Visibility.Visible)
                            {
                                gdFindAndReplace.Visibility = Visibility.Visible;
                                cmbFindText.UpdateLayout();
                            }
                            var editor = ActivedEditor;
                            if (editor != null)
                            {
                                cmbFindText.Text = editor.EditorBase.SelectedText;
                            }
                            else return;

                            cmbSearchArea.SelectedIndex = 0;

                            if (cmbFindText.Text.Length > 0)
                            {
                                cmbReplaceTextInputBox.Focus();
                            }
                            else
                            {
                                cmbFindText.Focus();
                            }
                            e.Handled = true;
                        }
                        //2016年3月9日，替换已整合到主界面了，没必要再用这个
                        //if (isCtrl && !isShift && !isAlt)
                        //{
                        //    var activeEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
                        //    if (activeEditor != null)
                        //    {
                        //        FindReplaceDialog.ShowForReplace(activeEditor.EditorBase, this);
                        //    }
                        //}
                        break;
                    }
                case Key.L:
                    {
                        if (isCtrl && isShift == false && isAlt == false)
                        {
                            SelecetLine();
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F3:
                    {
                        if (isCtrl == false && isAlt == false)
                        {
                            if (isShift)
                            {
                                FindPreview();
                            }
                            else
                            {
                                FindNext();
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.Escape:
                    {
                        if (isCtrl == false && isShift == false && isAlt == false)
                        {
                            var efi = this.mainTabControl.SelectedItem as MarkdownEditor;
                            if (efi != null) // && efi.EditorBase.IsSearchPanelOpened)
                            {
                                //CloseSearchPanel();//AvalonEdit的SearchPanel总是有些问题，放弃了
                                if (gdFindAndReplace.Visibility != Visibility.Collapsed)
                                {
                                    gdFindAndReplace.Visibility = Visibility.Collapsed;
                                    e.Handled = true;
                                    break;
                                }
                            }

                            if (this.WindowStyle == WindowStyle.None)
                            {
                                //全屏状态
                                SwitchFullScreen(false);
                            }
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.OemTilde://波形符键（反引号键）
                    {
                        if (isCtrl)
                        {
                            var mei = this.mainTabControl.SelectedItem as MarkdownEditor;
                            if (mei != null)
                            {
                                mei.EditorBase.WrapTextWithAntiQuotes();
                            }
                        }
                        break;
                    }
                case Key.OemPipe:
                    {
                        if (isCtrl && !isShift && !isAlt)
                        {
                            //插入新列
                            var selEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
                            if (selEditor != null)
                            {
                                selEditor.EditorBase.InsertTableColumn();
                                e.Handled = true;
                            }
                        }
                        break;
                    }
                case Key.Q:
                    {
                        //定位到资源搜索框，这样就可以直接输入文本了。
                        if (isCtrl && !isShift && !isAlt)
                        {
                            cmbSearchResource.Focus();
                        }
                        break;
                    }
                case Key.D1:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 1);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D2:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 2);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D3:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 3);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D4:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 4);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D5:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 5);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D6:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 6);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D7:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 7);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D8:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 8);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D9:
                    {
                        if (isCtrl && isAlt && !isShift)
                        {
                            ChangeWorkspaceByShortCut(isCtrl, isShift, isAlt, 9);
                            e.Handled = true;
                        }
                        break;
                    }
            }
        }

        private void ChangeWorkspaceByShortCut(bool isCtrl, bool isShift, bool isAlt, int num)
        {
            if (isAlt && isCtrl && !isShift)
            {
                var index = num - 1;
                if (index < 0 || index >= lbxHistoryWorkspaces.Items.Count)
                {
                    LMessageBox.Show("　　历史工作区中没有对应条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }

                lbxHistoryWorkspaces.SelectedIndex = index;

                if (tabControlLeftTools.SelectedIndex != 1)
                {
                    tabControlLeftTools.SelectedIndex = 1;
                }

                if (cdLeftToolsArea.ActualWidth < 140)
                {
                    cdLeftToolsArea.Width = new GridLength(140);
                }

                var item = lbxHistoryWorkspaces.Items[num - 1] as RecentDirectoryListBoxItem;
                if (item != null && item.DirectoryPath != Globals.PathOfWorkspace)
                {
                    var result = LMessageBox.Show("真的要把工作区切换到以下目录吗？\r\n\r\n　　" + item.DirectoryPath, Globals.AppName,
                         MessageBoxButton.YesNo, MessageBoxImage.Question);
                    if (result == MessageBoxResult.Yes)
                    {
                        ChangeWorkspace(item.DirectoryPath);
                    }
                }
            }
        }

        private void SwitchFullScreen(bool fullScreen)
        {
            if (fullScreen)
            {
                //cmbColor.Visibility =
                mainToolBar.Visibility =
                mainMenu.Visibility =
                leftToolBar.Visibility =
                rightToolBar.Visibility =
                bottomToolsDockpanel.Visibility = Visibility.Collapsed;

                cdLeftToolsArea.MinWidth = 0;
                cdLeftToolsArea.Width = new GridLength(0);
                cdRightToolsArea.Width = new GridLength(0, GridUnitType.Star);

                this.WindowState = System.Windows.WindowState.Maximized;

                //dpTitle.Visibility = Visibility.Collapsed;
                dpFind.Visibility = Visibility.Collapsed;

                gdFindAndReplace.Visibility = Visibility.Collapsed;

                this.ToolTip = "Esc或F11键退出全屏";
            }
            else
            {
                //cmbColor.Visibility =
                mainToolBar.Visibility =
                mainMenu.Visibility =
                leftToolBar.Visibility =
                rightToolBar.Visibility =
                bottomToolsDockpanel.Visibility = System.Windows.Visibility.Visible;

                cdLeftToolsArea.MinWidth = 40;
                cdLeftToolsArea.Width = new GridLength(2, GridUnitType.Star);
                cdMainEditArea.Width = new GridLength(3, GridUnitType.Star);
                cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);

                //通常不需要退出最大化状态。2016年2月28日
                //this.WindowState = System.Windows.WindowState.Normal;
                //this.Title = Globals.AppName;

                //dpTitle.Visibility = Visibility.Visible;
                dpFind.Visibility = Visibility.Visible;

                this.ToolTip = null;
            }
        }

        void mainTabControl_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            //处理与ExplorerBrowser可能冲突的快捷键，主要包括：
            //Ctrl+X,Ctrl+C,Ctrl+V,Ctrl+A,Ctrl+Z,Ctrl+Y,Del

            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            switch (e.Key)
            {
                case Key.D8:
                    {
                        if (isCtrl && !isAlt)
                        {
                            if (this.mainTabControl.SelectedItem != null)
                            {
                                var mdi = this.mainTabControl.SelectedItem as MarkdownEditor;
                                if (mdi != null)
                                {
                                    mdi.EditorBase.SwitchListMark(!isShift);
                                    e.Handled = true;
                                }
                            }
                        }

                        break;
                    }
            }
        }

        //有效
        //void NavigationLog_NavigationLogChanged(object sender, Microsoft.WindowsAPICodePack.Controls.NavigationLogEventArgs e)
        //{
        //    LMessageBox.Show(explorerBrowser.ExplorerBrowserControl.NavigationLog.CurrentLocation.ParsingName);
        //}

        //有效
        //void ExplorerBrowserControl_NavigationComplete(object sender, Microsoft.WindowsAPICodePack.Controls.NavigationCompleteEventArgs e)
        //{
        //    LMessageBox.Show(explorerBrowser.ExplorerBrowserControl.NavigationLog.CurrentLocation.ParsingName);
        //}

        //private Microsoft.WindowsAPICodePack.Shell.ShellFileSystemFolder pathOfAppDocumentsFolder =
        //    Microsoft.WindowsAPICodePack.Shell.ShellFileSystemFolder.FromFolderPath(Globals.PathofAppDocuments);

        private void tbxPathOfWorkspace_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                //explorerBrowser.ExplorerBrowserControl.Navigate(pathOfAppDocumentsFolder);
                if (Directory.Exists(Globals.PathOfWorkspace) == false)
                {
                    LMessageBox.Show(string.Format("　　指定的工作区目录[{0}]不存在。这可能是因为程序被安装到Window系统盘的某个目录中，又未获得管理员权限，导致无法创建工作区目录。",
                        Globals.PathOfWorkspace), Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                System.Diagnostics.Process.Start("explorer.exe", Globals.PathOfWorkspace);
            }
        }

        //private void NewDocument()
        //{
        //    var newEditor = new MarkdownEditor(string.Format("md_[{0:d4}]", mainTabControl.Items.Count + 1), this.IsExamEnabled);
        //    this.mainTabControl.Items.Insert(this.mainTabControl.SelectedIndex + 1, newEditor);
        //    this.mainTabControl.SelectedIndex += 1;
        //    newEditor.EditorBase.TextArea.Focus();
        //}

        private void btnNew_Click_1(object sender, RoutedEventArgs e)
        {
            //NewDocument();
            NewFile(false);
        }

        private void miNewdocument_Click_1(object sender, RoutedEventArgs e)
        {
            //NewDocument();
            NewFile(false);
        }

        private void miNewFolderDocument_Click(object sender, RoutedEventArgs e)
        {
            NewFile(false, true);
        }

        private void btnSave_Click_1(object sender, RoutedEventArgs e)
        {
            SaveDocment(miFormatBeforeSave.IsChecked);
        }

        private void miSave_Click_1(object sender, RoutedEventArgs e)
        {
            SaveDocment(miFormatBeforeSave.IsChecked);
        }

        private void OpenDocuments()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = Globals.AppName + "——打开文档：";
            ofd.Filter = Globals.DocumentName + "(*." + Globals.ExtensionName + ")|*." + Globals.ExtensionName;
            ofd.FilterIndex = 0;
            ofd.Multiselect = true;

            if (ofd.ShowDialog() != true) return;

            if (ofd.FileName.ToLower() != Globals.PathOfWorkspace)
            {
                var result = LMessageBox.Show("　　选择的文档不在默认工作区目录下，要复制文件副本到当前工作目录下并打开吗？", Globals.AppName,
                     MessageBoxButton.YesNoCancel, MessageBoxImage.Exclamation);
                switch (result)
                {
                    case MessageBoxResult.Yes:
                        {
                            //复制
                            string[] newFileNames = new string[ofd.FileNames.Length];
                            for (int i = 0; i < ofd.FileNames.Length; i++)
                            {
                                var s = ofd.FileNames[i];
                                var newFileName = Globals.PathOfWorkspace + ofd.SafeFileNames[i];
                                File.Copy(ofd.FileName, newFileName);
                                newFileNames[i] = newFileName;
                            }
                            OpenDocuments(newFileNames);
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            OpenDocuments(ofd.FileNames);
                            break;
                        }
                    case MessageBoxResult.Cancel: return;
                }
            }

        }

        public void OpenDocuments(string[] fullPathOfFiles)
        {
            List<string> files = new List<string>();
            foreach (string s in fullPathOfFiles)
            {
                files.Add(s);
            }
            OpenDocuments(files);
        }

        public void OpenDocuments(List<string> fullPathOfFiles)
        {
            StringBuilder errorMsg = new StringBuilder();

            foreach (string s in fullPathOfFiles)
            {
                MarkdownEditor openedFileItem = null;

                foreach (var item in this.mainTabControl.Items)
                {
                    MarkdownEditor eti = item as MarkdownEditor;
                    if (eti != null && eti.FullFilePath == s)
                    {
                        openedFileItem = eti;
                        break;
                    }
                }

                if (openedFileItem != null)
                {
                    this.mainTabControl.SelectedItem = openedFileItem;
                    continue;//已打开的文档，不再重复打开，只是使其成为活动文档。
                }

                MarkdownEditor newDocument = new MarkdownEditor("md_" + (mainTabControl.Items.Count + 1), this.IsExamEnabled);

                string openResult = newDocument.OpenDocument(s);
                if (openResult == string.Empty)
                {
                    this.mainTabControl.Items.Insert(this.mainTabControl.SelectedIndex + 1, newDocument);
                    this.mainTabControl.SelectedIndex += 1;
                }
                else
                {
                    errorMsg.Append(openResult);
                }
            }

            string em = errorMsg.ToString();
            if (em != string.Empty)
            {
                LMessageBox.Show("　　打开文件时出现异常。消息如下：\r\n" + em, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miOpen_Click_1(object sender, RoutedEventArgs e)
        {
            OpenDocuments();
        }

        private void btnOpen_Click_1(object sender, RoutedEventArgs e)
        {
            OpenDocuments();
        }

        private void miSaveAll_Click_1(object sender, RoutedEventArgs e)
        {
            SaveAllDocuments();
        }

        private void miExit_Click_1(object sender, RoutedEventArgs e)
        {
            this.Close();//关闭主窗口即退出。
        }

        private void miUndo_Click_1(object sender, RoutedEventArgs e)
        {
            Undo();
        }

        private void miRedo_Click_1(object sender, RoutedEventArgs e)
        {
            Redo();
        }

        private void miCut_Click_1(object sender, RoutedEventArgs e)
        {
            Cut();
        }

        private void miCopy_Click_1(object sender, RoutedEventArgs e)
        {
            Copy();
        }

        private void miPaste_Click_1(object sender, RoutedEventArgs e)
        {
            Paste();
        }

        private void miPasteCode_Click(object sender, RoutedEventArgs e)
        {
            PasteCode();
        }

        public void PasteCode()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.PasteCode();
        }

        private void miSelectAll_Click_1(object sender, RoutedEventArgs e)
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.SelectAll();
        }

        private void btnUndo_Click_1(object sender, RoutedEventArgs e)
        {
            Undo();
        }

        private void btnRedo_Click_1(object sender, RoutedEventArgs e)
        {
            Redo();
        }

        public void Cut()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.Cut();
        }

        private void btnCut_Click_1(object sender, RoutedEventArgs e)
        {
            Cut();
        }

        public void Copy()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.Copy();
        }

        private void btnCopy_Click_1(object sender, RoutedEventArgs e)
        {
            Copy();
        }

        public void Paste()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.Paste();
        }

        private void btnPaste_Click_1(object sender, RoutedEventArgs e)
        {
            Paste();
        }

        private void btnPasteCode_Click(object sender, RoutedEventArgs e)
        {
            PasteCode();
        }

        private void RefreshFontSize()
        {
            foreach (var item in this.mainTabControl.Items)
            {
                MarkdownEditor eti = item as MarkdownEditor;
                if (eti == null) continue;

                eti.EditorBase.Options.WordWrapIndentation = this.mainTabControl.FontSize * 2;
            }

            foreach (var item in this.rightToolBar.Items)
            {
                MarkdownEditor eti = item as MarkdownEditor;
                if (eti == null) continue;

                eti.EditorBase.Options.WordWrapIndentation = this.mainTabControl.FontSize * 2;
            }
        }

        internal void FontSizeUp()
        {
            if (this.mainTabControl.FontSize <= 98)
            {
                this.mainTabControl.FontSize += 2;//最大字号100。

                //右工具栏中的对照区不能直接对右工具栏变化字号
                foreach (var item in this.rightToolBar.Items)
                {
                    MarkdownEditor eti = item as MarkdownEditor;
                    if (eti == null) continue;

                    eti.EditorBase.FontSize = this.mainTabControl.FontSize;
                }

                this.ConfigManager.Set("FontSize", this.mainTabControl.FontSize.ToString());
                RefreshFontSize();
            }
        }

        private void btnFontSizeUp_Click_1(object sender, RoutedEventArgs e)
        {
            FontSizeUp();
        }

        internal void FontSizeDown()
        {
            if (this.mainTabControl.FontSize >= 8)
            {
                this.mainTabControl.FontSize -= 2;//最小字号6。

                //右工具栏中的对照区不能直接对右工具栏变化字号
                foreach (var item in this.rightToolBar.Items)
                {
                    MarkdownEditor eti = item as MarkdownEditor;
                    if (eti == null) continue;

                    eti.EditorBase.FontSize = this.mainTabControl.FontSize;
                }

                this.ConfigManager.Set("FontSize", this.mainTabControl.FontSize.ToString());
                RefreshFontSize();
            }
        }

        private void btnFontSizeDown_Click_1(object sender, RoutedEventArgs e)
        {
            FontSizeDown();
        }

        private void miFontSizeUp_Click_1(object sender, RoutedEventArgs e)
        {
            FontSizeUp();
        }

        private void miFontSizeDown_Click_1(object sender, RoutedEventArgs e)
        {
            FontSizeDown();
        }

        private void miFontSizeReset_Click_1(object sender, RoutedEventArgs e)
        {
            this.mainTabControl.FontSize = 16;//默认字号。
            RefreshFontSize();
        }

        private void WrapWithBold()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.SelectedText.Contains("\r") || eti.EditorBase.SelectedText.Contains("\n"))
            {
                LMessageBox.Show("  这个操作不允许跨行！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            eti.EditorBase.Bold();
        }

        private void WrapWithItalic()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.SelectedText.Contains("\r") || eti.EditorBase.SelectedText.Contains("\n"))
            {
                LMessageBox.Show("  这个操作不允许跨行！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            eti.EditorBase.Italic();
        }

        private void WrapWithUTag()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.SelectedText.Contains("\r") || eti.EditorBase.SelectedText.Contains("\n"))
            {
                LMessageBox.Show("  这个操作不允许跨行！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            eti.EditorBase.SelectedText = "<u>" + eti.EditorBase.SelectedText + "</u>";
        }

        private void WrapWithSTag()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.EditorBase.SelectedText.Contains("\r") || eti.EditorBase.SelectedText.Contains("\n"))
            {
                LMessageBox.Show("  这个操作不允许跨行！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            eti.EditorBase.SelectedText = "[=" + eti.EditorBase.SelectedText + "=]";
        }

        //private void Find()
        //{
        //    //AvalonEdit的查找功能有Bug:无法输入中文。
        //    //if (this.mainTabControl.SelectedItem == null) return;
        //    //MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
        //    //if (eti == null) return;
        //    //eti.EditorBase.Find();

        //    rightToolBar.SelectedIndex = 1;
        //    cmbFindText.Focus();
        //}

        private void FindPreview()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.FindPreview();
        }

        private void FindNext()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.FindNext();
        }

        private void CloseSearchPanel()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.CloseSearchPanel();
        }

        private void SelecetLine()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.SelectLine();
        }


        private void miSelectLine_Click_1(object sender, RoutedEventArgs e)
        {
            SelecetLine();
        }

        private void miAbout_Click_1(object sender, RoutedEventArgs e)
        {
            AboutBox abox = new AboutBox(this);
            abox.ShowDialog();
        }

        private void btnSaveAll_Click(object sender, RoutedEventArgs e)
        {
            SaveAllDocuments();
        }

        private void previewHtml_Click(object sender, RoutedEventArgs e)
        {
            CompileAndPreviewHtml(true);
        }

        public void CompileAndPreviewHtml(bool callSystemDefaultExplorer = false)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            if (eti.IsModified)
            {
                var messageResult = LMessageBox.Show("　　文档需要先保存，要继续吗？",
                    Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (messageResult != MessageBoxResult.Yes) return;

                if (Globals.MainWindow.miFormatBeforeSave.IsChecked)
                {
                    eti.GetFormatedMarkdownText();
                }

                var result = eti.SaveDocument();
                if (result != string.Empty) return;
            }

            if (callSystemDefaultExplorer == false)
            {
                eti.CompileAndPreviewHtml();

                rightToolBar.SelectedIndex = 0;
                if (rightToolBar.Visibility != System.Windows.Visibility.Visible ||
                                rightToolBar.ActualWidth <= 0)
                {
                    SwitchRightToolBarToggle();
                }
            }
            else
            {
                eti.CompileAndPreviewHtml();
            }
        }

        private void miNewFile_Click(object sender, RoutedEventArgs e)
        {
            NewFile(false);
        }

        private void miNewSampleFile_Click(object sender, RoutedEventArgs e)
        {
            NewFile(true);
        }

        private void NewFile(bool isSampleFile, bool isMataFile = false)
        {
            WorkspaceTreeViewItem wtvi;
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先在主界面左侧工作区浏览窗口中选定一个目录来创建文件。" +
                    "如果看不到工作区浏览窗口，请按F12键显示左工具栏。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                return;

                //if (tvWorkDirectory.Items.Count <= 0) return;
                //wtvi = tvWorkDirectory.Items[0] as WorkspaceTreeViewItem;
            }
            else
            {
                wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            }

            string directoryPath;
            WorkspaceTreeViewItem destWtvi;

            if (wtvi.IsMarkdownFilePath)
            {
                directoryPath = wtvi.ParentDirectory;
                destWtvi = wtvi.ParentWorkspaceTreeViewItem;
            }
            else
            {
                directoryPath = wtvi.FullPath;
                destWtvi = wtvi;
            }

            if (Directory.Exists(directoryPath) == false)
            {
                var lastIndex = directoryPath.LastIndexOf("\\");
                if (lastIndex < 0)
                {
                    LMessageBox.Show("　　只能在目录下新建文件。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                var parentDirectory = directoryPath.Substring(0, lastIndex);
                if (Directory.Exists(parentDirectory) == false)
                {
                    LMessageBox.Show("　　只能在目录下新建文件。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                directoryPath = parentDirectory;
            }

            if (directoryPath.EndsWith("~") || directoryPath.EndsWith("~\\"))
            {
                LMessageBox.Show("　　以波形符结尾的目录是程序自动添加的资源目录，不能在其中再建立MarkDown文件。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (directoryPath.EndsWith("\\") == false) directoryPath += "\\";

            string newShortFileName;

            if (isMataFile)
            {
                newShortFileName = "_" + new DirectoryInfo(directoryPath).Name + ".md";
            }
            else
            {
                newShortFileName = InputBox.Show(Globals.AppName, "请输入新文件名：", "", true,
                   "说明：\r\n　　⑴不需要输入后缀名；\r\n　　⑵请尽可能设定有意义的文件名。\r\n　　因为此文件名很可能将来被其它文件引用。所以，如非必要，创建之后请勿随意更改文件名。");
            }

            if (newShortFileName == null) return;//用户放弃新建文件。

            if (newShortFileName.Contains(".") == false) newShortFileName += ".md";

            if (string.IsNullOrEmpty(newShortFileName))
            {
                LMessageBox.Show("　　文件名称不能为空！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            try
            {
                var newFilePath = directoryPath + newShortFileName;
                using (StreamWriter sw = File.CreateText(newFilePath))
                {
                    if (isSampleFile)
                    {
                        sw.Write(Properties.Resources.example);
                    }
                    else
                    {
                        var docTitle = newShortFileName.EndsWith(".md") ? newShortFileName.Substring(0, newShortFileName.Length - 3) : newShortFileName;
                        sw.Write($"\r\n%{docTitle}\r\n\r\n；{DateTime.Now.ToString()}\r\n\r\n");
                    }
                }

                WorkspaceTreeViewItem newtvi = new WorkspaceTreeViewItem(newFilePath, Globals.MainWindow);
                if (isMataFile == false)
                {
                    destWtvi.Items.Add(newtvi);
                }
                else
                {
                    destWtvi.Items.Insert(0, newtvi);
                }
                newtvi.IsSelected = true;

                OpenDocuments(new string[] { newFilePath });

                //TODO: 使编辑器获取焦点
                var efi = ActivedEditor;
                efi.EditorBase.UpdateLayout();
                efi.EditorBase.Select(efi.EditorBase.Text.Length, 0);
                efi.UpdateLayout();
                efi.EditorBase.Focus();
            }
            catch (Exception ex)
            {
                LMessageBox.Show("　　新建文件失败！错误消息：\r\n" + ex.Message,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void miDeleteFile_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中要删除的文件。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;

            if (wtvi.FullPath.EndsWith("~") || wtvi.FullPath.EndsWith("~\\") || wtvi.FullPath == Globals.PathOfWorkspace)
            {
                LMessageBox.Show("　　这个文件或目录是程序自动生成的，不能直接删除。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
            try
            {
                if (Directory.Exists(wtvi.FullPath))
                {
                    var result = LMessageBox.Show("　　删除目录会删除其中的所有文件和子目录。真的要删除此目录吗？", Globals.AppName,
                        MessageBoxButton.YesNo, MessageBoxImage.Warning);
                    if (result != MessageBoxResult.Yes) return;

                    Directory.Delete(wtvi.FullPath, true);
                    workspaceManager.Refresh(null);
                    return;
                }

                if (File.Exists(wtvi.FullPath))
                {
                    if (wtvi.IsImageFileExist)
                    {
                        ImagePreview.Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/App.png"));
                        File.Delete(wtvi.FullPath);

                        var parentItem = (wtvi.Parent as TreeViewItem);
                        parentItem.Items.Remove(wtvi);
                        parentItem.IsSelected = true;
                    }
                    else if (wtvi.IsMarkdownFilePath)
                    {
                        try
                        {
                            DirectoryInfo resourceDirectoryInfo = new DirectoryInfo(wtvi.ResourceDirectoryFullPath);

                            MarkdownEditor openedEditor = null;
                            foreach (var item in this.mainTabControl.Items)
                            {
                                var editor = item as MarkdownEditor;
                                if (editor == null) continue;

                                if (editor.FullFilePath.ToLower() == wtvi.FullPath.ToLower())
                                {
                                    openedEditor = editor;
                                    break;
                                }
                            }

                            if (openedEditor != null)
                            {
                                this.mainTabControl.Items.Remove(openedEditor);
                            }

                            File.Delete(wtvi.FullPath);

                            if (Directory.Exists(resourceDirectoryInfo.FullName))
                            {
                                Directory.Delete(resourceDirectoryInfo.FullName, true);
                            }

                            var parentItem = (wtvi.Parent as TreeViewItem);
                            parentItem.Items.Remove(wtvi);
                            parentItem.IsSelected = true;
                        }
                        catch (Exception ex)
                        {
                            LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miInsertLinkToCurrentDocument_Click(object sender, RoutedEventArgs e)
        {
            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi == null)
            {
                LMessageBox.Show("请先在工作区目录中选择一个文件作为要链接的目标文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (wtvi.IsMarkdownFilePath == false)
            {
                if (wtvi.IsImageFileExist)
                {
                    wtvi.InsertImageTagToDocument(wtvi.FullPath, true);
                    return;
                }

                LMessageBox.Show("只有Markdown文件、图片资源文件才能链接（且需要编译为Html文档才有效）！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //将当前选定文档作为一个链接插入到正在编辑的文档中。
            var editor = Globals.MainWindow.ActivedEditor;
            if (editor == null)
            {
                LMessageBox.Show("当前没有打开任何文档，无法添加对此文件的引用！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var destFilePath = editor.FullFilePath;
            var srcPath = wtvi.FullPath.ToLower();
            string shortName;

            if (File.Exists(wtvi.FullPath))
            {
                FileInfo fi = new FileInfo(wtvi.FullPath);
                shortName = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length);
            }
            else if (Directory.Exists(wtvi.FullPath))
            {
                DirectoryInfo di = new DirectoryInfo(wtvi.FullPath);
                shortName = di.Name;
            }
            else shortName = "";

            editor.EditorBase.SelectedText = BuildLinkText(srcPath, destFilePath, shortName);
            editor.EditorBase.Select(editor.EditorBase.SelectionStart + 1, Math.Max(shortName.Length, 0));
        }

        private void miFindAllLinkToThisFile_Click(object sender, RoutedEventArgs e)
        {
            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi == null)
            {
                LMessageBox.Show("请先在工作区目录中选择一个文件作为要查找的文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (wtvi.IsMarkdownFilePath == false && wtvi.IsImageFileExist == false)
            {
                LMessageBox.Show("只有Markdown文件、图片资源文件才能链接（且需要编译为Html文档才有效）！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            tvFindAndReplace.Items.Clear();

            FileInfo fi = new FileInfo(wtvi.FullPath);
            var shortName = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length);
            var item = new FindDocumentTreeViewItem(wtvi.FullPath, wtvi.ShortName, $"{wtvi.ShortName} 被引用于：");

            FindAllLinksToThisFile(item, fi.FullName, Globals.PathOfWorkspace, shortName);

            if (item.HasItems)
            {
                tvFindAndReplace.Items.Add(item);
                item.IsExpanded = true;
            }
            else
            {
                tvFindAndReplace.Items.Add(new TreeViewItem() { Header = "<没有找到任何引用...>" });
            }

            rightToolBar.SelectedIndex = 1;
        }

        private void FindAllLinksToThisFile(FindDocumentTreeViewItem item, string srcPath, string destDirectory, string shortName)
        {
            if (File.Exists(srcPath) == false) return;

            if (Directory.Exists(destDirectory) == false) return;

            var directoryInfo = new DirectoryInfo(destDirectory);
            var files = directoryInfo.GetFiles();

            foreach (var file in files)
            {
                if (file.Extension.ToLower() != ".md" &&
                   WorkspaceTreeViewItem.IsValidateImageFilePath(file.FullName) == false) continue;

                var linkText = BuildLinkText(srcPath, file.FullName, shortName);
                //BuildLinkText()其实只针对文字链接，但稍加修改也可以用于图像文本链接，
                //因为图像文件链接只在头部多个！号而已。

                var edit = GetOpenedEditor(file.FullName);
                string[] lines;
                if (edit != null)
                {
                    //打开的文件，以正在编辑的内容为准
                    lines = edit.EditorBase.Text.Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.None);
                }
                else
                {
                    //没打开的文件，以磁盘数据为准
                    lines = File.ReadAllLines(file.FullName);
                }

                var lineNumber = 0;
                foreach (var line in lines)
                {
                    lineNumber++;
                    var index = line.ToLower().IndexOf(linkText.ToLower());
                    if (index >= 0)
                    {
                        var length = linkText.Length;
                        if (WorkspaceTreeViewItem.IsValidateImageFilePath(srcPath) && index >= 1)
                        {
                            index -= 1;//图形的链接文本与普通链接相比，前面多个!号，故索引需要变化一下。
                            length += 1;
                        }
                        item.Items.Add(new FindLineTreeViewItem(file.FullName, file.Name, lineNumber, index, length,
                            $"文件：{file.Name}，第 {lineNumber} 行", null, FindLineTreeViewItem.ItemType.Normal));
                    }
                }
            }

            var subDirectories = directoryInfo.GetDirectories();
            foreach (var subDirectory in subDirectories)
            {
                if (subDirectory.FullName.EndsWith("~")) continue;

                var subDirectoryItem = new FindDocumentTreeViewItem(subDirectory.FullName, subDirectory.Name);
                FindAllLinksToThisFile(subDirectoryItem, srcPath, subDirectory.FullName, subDirectory.Name);

                if (subDirectoryItem.HasItems)
                {
                    subDirectoryItem.IsExpanded = true;
                    item.Items.Add(subDirectoryItem);
                }
            }
        }

        /// <summary>
        /// 取引用链接文本。
        /// </summary>
        /// <param name="resourceFullPathName">是指将被引用的资源的文件路径</param>
        /// <param name="mdFileFullPathName">resourceFullPathName。</param>
        /// <param name="shortName">资源短名，通常是文件名。</param>
        /// <returns>生成的用于插入到<para>mdFileFullPathName</para>指向的Md文件中的链接的文本。</returns>
        private string BuildLinkText(string resourceFullPathName, string mdFileFullPathName, string shortName)
        {
            //if (resourceFullPathName != null)
            //{
            //    resourceFullPathName = resourceFullPathName.ToLower();
            //}

            //if (mdFileFullPathName != null)
            //{
            //    mdFileFullPathName = mdFileFullPathName.ToLower();
            //}

            if (resourceFullPathName.EndsWith(".md"))
            {
                resourceFullPathName = resourceFullPathName.Substring(0, resourceFullPathName.Length - 3) + ".html";
            }

            var workspacePath = Globals.PathOfWorkspace.ToLower();

            //思路：先找出共同的、最接近的祖先级目录
            var separator = new char[] { '\\', '/' };
            string[] src = resourceFullPathName.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            string[] dest = mdFileFullPathName.Split(separator, StringSplitOptions.RemoveEmptyEntries);

            var minIndex = Math.Min(src.Length, dest.Length) - 1;

            if (minIndex < 0) return "";

            int aIndex = -1;
            for (int i = 0; i <= minIndex; i++)
            {
                if (src[i].ToLower() != dest[i].ToLower())
                {
                    aIndex = i;
                    break;
                }
            }

            //生成相对路径的前半部分，类似（../../）这样子。
            string[] file = mdFileFullPathName.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            string header;
            if (resourceFullPathName == mdFileFullPathName || aIndex < 0)//同一文件内部引用
            {
                header = "";
            }
            else
            {
                header = FindLineTreeViewItem.BuildHtmlRefText(file.Length - aIndex - 1);

                for (int i = aIndex; i < src.Length; i++)
                {
                    header += src[i];
                    header += "/";
                }

                header = header.Replace(".md", ".html");
                if (header.EndsWith("/"))
                {
                    header = header.Substring(0, header.Length - 1);
                }
            }

            if (shortName.EndsWith(".md"))
            {
                shortName = shortName.Substring(0, shortName.Length - 3);
            }

            var linkText = "[" + shortName + "](" + header + ")";
            return linkText;
        }

        private void miNewDirectory_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中要在哪个目录下新建子目录。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (Directory.Exists(wtvi.FullPath) == false)
            {
                LMessageBox.Show("　　只能在目录下新建子目录。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            DirectoryInfo di = new DirectoryInfo(wtvi.FullPath);
            if (di.Name.EndsWith("~"))
            {
                LMessageBox.Show("　　以波形符结尾的目录是程序自动管理的资源目录，不允许在其下新建子目录。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var newShortDirectoryName = InputBox.Show(Globals.AppName, "请输入新目录名：", "", true);
            if (string.IsNullOrEmpty(newShortDirectoryName))
            {
                LMessageBox.Show("　　目录名称不能为空！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            try
            {
                var newFullPath = (wtvi.FullPath.EndsWith("\\") ? wtvi.FullPath : (wtvi.FullPath + "\\")) + newShortDirectoryName;
                if (Directory.Exists(newFullPath) == false)
                {
                    Directory.CreateDirectory(newFullPath);
                }
                var newItem = new WorkspaceTreeViewItem(newFullPath, Globals.MainWindow);
                wtvi.Items.Add(newItem);
                newItem.IsSelected = true;
            }
            catch (Exception ex)
            {
                LMessageBox.Show("　　创建目录失败！错误消息：\r\n" + ex.Message,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);

            }
        }

        private void mainTabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            if (mainTabControl.SelectedItem == null) return;
            MarkdownEditor me = mainTabControl.SelectedItem as MarkdownEditor;
            if (me == null) return;

            me.EditorBase.Focus();
        }

        private void cmbColor_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (configManager == null || cmbColor.SelectedItem == null) return;

            var colorName = (cmbColor.SelectedItem as ComboBoxItem).Tag.ToString();
            configManager.Set("color", colorName);

            if (colorName == "light")
            {
                imagePreviewOutBorder.Background = Brushes.White;
            }
            else if (colorName == "dark")
            {
                imagePreviewOutBorder.Background = new SolidColorBrush(Color.FromRgb(0x3D, 0x3D, 0x3D));
            }
            else
            {
                imagePreviewOutBorder.Background = Brushes.Transparent;
            }
        }

        private void miRename_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中要重命名的项目。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;

            var result = LMessageBox.Show("　　重命名文件（或目录）会导致所有对此文件（或目录）的引用（链接）失效。\r\n　　" +
                "> 对与此文件相关的资源文件的引用也会失效。\r\n\r\n" +
                "　　建议先使用“查找引用”功能看看此文件（或此目录）是否被引用过。\r\n\r\n" +
                "　　要自动查找一下可能存在的引用么？（要注意可能出现同名文件被引用！）",
                Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning);
            if (result == MessageBoxResult.Yes)
            {
                cmbFindText.Text = wtvi.ShortName;
                cmbSearchArea.SelectedIndex = 2;
                FindText();
                return;
            }

            if (wtvi.IsDirectoryExists)
            {
                if (wtvi.FullPath == Globals.PathOfWorkspace)
                {
                    LMessageBox.Show("　　不能重命名工作区目录！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                if (wtvi.FullPath.EndsWith("~") || wtvi.FullPath.EndsWith("~\\"))
                {
                    LMessageBox.Show("　　这是程序预定的资源目录名，不可修改！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                var newShortName = InputBox.Show(Globals.AppName, "请输入新目录名称：", wtvi.ShortName, true);
                if (string.IsNullOrEmpty(newShortName))
                {
                    LMessageBox.Show("　　指定的目录名不合法。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                try
                {
                    #region 在重命名一个目录之前，先将该目录对应的元文件、元文件资源文件夹、编译后的html元文件重命名。
                    var oldFullPath = wtvi.FullPath;
                    if (oldFullPath.EndsWith("\\") == false) oldFullPath += "\\";

                    var newFullPath = wtvi.FullPath;
                    if (newFullPath.EndsWith("\\")) newFullPath = newFullPath.Substring(0, newFullPath.Length - 1);

                    var lastindex = newFullPath.LastIndexOf("\\");
                    if (lastindex >= 0) newFullPath = newFullPath.Substring(0, lastindex);
                    var oldShortName = newFullPath.Substring(lastindex + 1);

                    var oldMataFileShortName = "_" + oldShortName + ".md";
                    var oldMataFileFullPath = oldFullPath + oldMataFileShortName;
                    var oldMataFileResourceDirectoryPath = oldFullPath + oldShortName + "~\\";
                    var oldMataHtmlFileFullPath = oldFullPath + "_" + oldShortName + ".html";

                    File.Move(oldMataFileFullPath, oldFullPath + "_" + newShortName + ".md");
                    Directory.Move(oldMataFileResourceDirectoryPath, oldFullPath + newShortName + "~");
                    File.Move(oldMataHtmlFileFullPath, oldFullPath + "_" + newShortName + ".html");
                    #endregion

                    //然后再命名文件夹本身。
                    newFullPath = newFullPath + "\\" + newShortName + "\\";
                    Directory.Move(wtvi.FullPath, newFullPath);
                    wtvi.FullPath = newFullPath;
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }
            else if (wtvi.IsFileExists)
            {
                var newShortName = InputBox.Show(Globals.AppName, "请输入新文件名：", wtvi.ShortName, true);
                if (newShortName == null) return;

                if (newShortName == "" || newShortName.ToLower() == ".md")
                {
                    LMessageBox.Show("　　指定的文件名不能为空。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                if (newShortName.ToLower().EndsWith(".md"))
                {
                    newShortName = newShortName.Substring(0, newShortName.Length - 3);
                }

                try
                {
                    var newFullPath = wtvi.FullPath;
                    if (newFullPath.EndsWith("\\")) newFullPath = newFullPath.Substring(0, newFullPath.Length - 1);

                    var lastindex = newFullPath.LastIndexOf("\\");
                    if (lastindex >= 0) newFullPath = newFullPath.Substring(0, lastindex);

                    newFullPath = newFullPath + "\\" + newShortName;

                    if (wtvi.IsMarkdownFilePath && newFullPath.ToLower().EndsWith(".md") == false) newFullPath += ".md";

                    File.Move(wtvi.FullPath, newFullPath);
                    var oldShortName = wtvi.ShortName;

                    var oldFilePath = wtvi.FullPath;
                    wtvi.FullPath = newFullPath;

                    //更新打开的文档的对应编辑器的FullPath
                    foreach (var item in Globals.MainWindow.mainTabControl.Items)
                    {
                        MarkdownEditor me = item as MarkdownEditor;
                        if (me == null) continue;

                        if (me.FullFilePath == oldFilePath)
                        {
                            me.FullFilePath = newFullPath;
                            break;
                        }
                    }

                    if (wtvi.FullPath.ToLower().EndsWith(".md"))
                    {
                        var parentDirectoryPath = wtvi.FullPath.Substring(0, wtvi.FullPath.LastIndexOf("\\"));
                        if (parentDirectoryPath.EndsWith("\\") == false) parentDirectoryPath += "\\";

                        var directories = new DirectoryInfo(parentDirectoryPath).GetDirectories();
                        foreach (var directory in directories)
                        {
                            var directoryShortName = oldShortName.Substring(0, oldShortName.Length - 3);
                            if (directory.FullName.EndsWith(directoryShortName) || directory.FullName.EndsWith(directoryShortName + "~"))
                            {
                                var dest = directory.FullName;
                                if (dest.EndsWith("\\")) dest.Substring(0, dest.Length - 1);

                                int lastIndex = dest.LastIndexOf("\\");
                                dest = dest.Substring(0, lastindex + 1);

                                directory.MoveTo(dest + newShortName + "~\\");
                                break;
                            }
                        }

                        var htmlFilePath = oldFilePath.Substring(0, oldFilePath.Length - 3) + ".html";

                        if (File.Exists(htmlFilePath))
                        {
                            File.Copy(htmlFilePath, parentDirectoryPath + newShortName + ".html");
                        }
                    }
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }

            workspaceManager.Refresh(wtvi.FullPath);
        }

        private void tvWorkDirectory_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Delete:
                    {
                        miDeleteFile_Click(sender, e);
                        e.Handled = true;
                        break;
                    }
                case Key.Enter:
                    {
                        if (tvWorkDirectory.SelectedItem != null)
                        {
                            var wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
                            if (wtvi != null)
                            {
                                if (wtvi.IsMarkdownFilePath)
                                {
                                    wtvi.OpenFile();
                                    e.Handled = true;
                                }
                                else if (wtvi.IsDirectoryExists)
                                {
                                    wtvi.IsExpanded = true;
                                    e.Handled = true;
                                }
                                else if (wtvi.IsImageFileExist)
                                {
                                    wtvi.InsertImageTagToDocument(wtvi.FullPath);
                                    e.Handled = true;
                                }
                            }
                        }
                        break;
                    }
                case Key.F2:
                    {
                        miRename_Click(sender, e);
                        break;
                    }
            }
        }

        private void miFormatDocument_Click(object sender, RoutedEventArgs e)
        {
            Format();
        }

        private void Format()
        {
            if (this.mainTabControl.SelectedItem == null) return;

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.Format();
        }

        public MarkdownEditor SelectedMarkdownEditor
        {
            get
            {
                if (this.mainTabControl.Items.Count <= 0) return null;
                return this.mainTabControl.SelectedItem as MarkdownEditor;
            }
        }

        public WorkspaceTreeViewItem FindWorkspaceTreeViewItem(string fullPath)
        {
            if (string.IsNullOrEmpty(fullPath)) return null;

            var baseItem = tvWorkDirectory.Items[0] as WorkspaceTreeViewItem;//根节点，必然存在。

            if (baseItem.FullPath.ToLower() == fullPath.ToLower()) return baseItem;

            foreach (var item in baseItem.Items)
            {
                WorkspaceTreeViewItem wtvi = item as WorkspaceTreeViewItem;
                if (wtvi == null) continue;

                if (wtvi.FullPath.ToLower() == fullPath.ToLower()) return wtvi;

                var resultItem = SearchWorkspaceTreeViewItem(wtvi, fullPath);
                if (resultItem != null) return resultItem;
            }

            return null;
        }

        private WorkspaceTreeViewItem SearchWorkspaceTreeViewItem(WorkspaceTreeViewItem item, string fullPath)
        {
            if (item == null || string.IsNullOrEmpty(fullPath)) return null;

            if (item.FullPath.ToLower() == fullPath.ToLower()) return item;

            foreach (var childItem in item.Items)
            {
                WorkspaceTreeViewItem wtvi = childItem as WorkspaceTreeViewItem;
                if (wtvi == null) continue;

                var resultItem = SearchWorkspaceTreeViewItem(wtvi, fullPath);
                if (resultItem != null) return resultItem;
            }

            return null;
        }

        private void miFormatTextTable_Click(object sender, RoutedEventArgs e)
        {
            FormatAsTextTable();
        }

        private void FormatAsTextTable()
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.FormatTextTable();
        }

        private XmlConfigManager workspaceConfigManager = null;

        public XmlConfigManager WorkspaceConfigManager
        {
            get
            {
                if (workspaceConfigManager == null)
                {
                    workspaceConfigManager = new XmlConfigManager(Globals.PathOfWorkspace + "Configure.xml");
                }

                return workspaceConfigManager;
            }
        }

        public void ChangeWorkspace(string newWorkspacePath = null)
        {
            //检查文件保存状态
            try
            {
                List<MarkdownEditor> needSavingDocumentList = new List<MarkdownEditor>();
                foreach (var item in this.mainTabControl.Items)
                {
                    MarkdownEditor eti = item as MarkdownEditor;
                    if (eti != null && eti.IsModified)
                    {
                        needSavingDocumentList.Add(eti);
                    }
                }

                if (needSavingDocumentList.Count > 0)
                {
                    MessageBoxResult r = LMessageBox.Show(string.Format("　　有 {0} 个文档已被修改，要保存吗？", needSavingDocumentList.Count),
                        Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
                    switch (r)
                    {
                        case MessageBoxResult.Yes:
                            {
                                string saveInfo;
                                foreach (MarkdownEditor eti in needSavingDocumentList)
                                {
                                    saveInfo = eti.SaveDocument();
                                    if (saveInfo == "用户取消操作")
                                    {
                                        return;//不更改工作目录
                                    }
                                    else if (saveInfo != string.Empty)
                                    {
                                        LMessageBox.Show(saveInfo, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                                        return;//不更改工作目录
                                    }
                                }

                                this.mainTabControl.Items.Clear();
                                break;
                            }
                        case MessageBoxResult.Cancel: return;//不更改工作目录。
                        default://No,放弃保存，更改工作目录。
                            {
                                break;
                            }
                    }
                }

                //写入当前打开的文档列表。
                RememberOpenedFilesForWorkspace();

                this.mainTabControl.Items.Clear();
                this.workspaceConfigManager = null;//重置工作区配置文件管理器。

                string destWorkspaceFolder = newWorkspacePath;

                if (destWorkspaceFolder == null)
                {
                    //选择新工作目录
                    System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog()
                    {
                        Description = "　　请选择一个目录作为新的工作区目录。\r\n　　一些必要的css、js和图像文件会被复制到指定的目录中。",
                        ShowNewFolderButton = true,
                    };
                    System.Windows.Interop.HwndSource source = PresentationSource.FromVisual(this) as System.Windows.Interop.HwndSource;

                    System.Windows.Forms.IWin32Window win = new WinFormWindow(source.Handle);
                    System.Windows.Forms.DialogResult result = fbd.ShowDialog(win);
                    if (result.Equals(System.Windows.Forms.DialogResult.OK) && Directory.Exists(fbd.SelectedPath))
                    {
                        destWorkspaceFolder = fbd.SelectedPath;
                    }
                    else return;//相当于用户取消了操作。
                }
                else
                {
                    if (Directory.Exists(destWorkspaceFolder) == false)
                    {
                        var result = LMessageBox.Show("　　物理磁盘上不存在该目录，要创建一个新目录吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                        if (result != MessageBoxResult.Yes) return;

                        System.IO.Directory.CreateDirectory(destWorkspaceFolder);
                    }
                }

                this.configManager.Set("Workspace", destWorkspaceFolder);
                Globals.PathOfWorkspace = destWorkspaceFolder;
                this.tbxPathOfWorkspace.Text = destWorkspaceFolder;

                CopyCssAndResourceFiles(destWorkspaceFolder);
                this.workspaceManager.Refresh(null);

                previewFrame.Source = null;

                //记载到一个列表文件中
                // var lines = File.ReadAllLines(Globals.PathOfHistoryWorkspaceFileFullName);
                WriteHistoryWorkspacesFile(destWorkspaceFolder);

                for (int i = lbxHistoryWorkspaces.Items.Count - 1; i >= 0; i--)
                {
                    var rwitem = lbxHistoryWorkspaces.Items[i] as RecentDirectoryListBoxItem;
                    if (rwitem != null && rwitem.DirectoryPath == destWorkspaceFolder)
                    {
                        lbxHistoryWorkspaces.Items.Remove(rwitem);
                    }
                }

                var rw = new RecentDirectoryListBoxItem(destWorkspaceFolder);
                rw.MouseDoubleClick += rw_MouseDoubleClick;

                lbxHistoryWorkspaces.Items.Insert(0, rw);
                lbxHistoryWorkspaces.SelectedIndex = 0;

                tabControlLeftTools.SelectedIndex = 0;

                RefreshWorkspaceHistoryList();

                LoadRememberOpenedFiles();//载入上次打开的文件。
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void RefreshWorkspaceHistoryList()
        {
            for (int i = 0; i < lbxHistoryWorkspaces.Items.Count; i++)
            {
                var item = lbxHistoryWorkspaces.Items[i] as RecentDirectoryListBoxItem;
                if (item == null) continue;

                item.IndexText = (i + 1).ToString();
            }
        }

        private void LoadRememberOpenedFiles()
        {
            if (this.RememberOpenedFiles == false) return;

            var openedFilesText = this.WorkspaceConfigManager.Get("OpenedFiles");
            var rememberedActiveDocumentFullPath = this.WorkspaceConfigManager.Get("ActiveDocumentFullPath");
            //文档初始化

            if (string.IsNullOrEmpty(openedFilesText) || this.RememberOpenedFiles == false)
            {
                var newEditor = new MarkdownEditor("md_0001.md", this.IsExamEnabled);
                this.mainTabControl.Items.Add(newEditor);
                this.mainTabControl.SelectedIndex = 0;
                newEditor.EditorBase.Focus();
            }
            else
            {
                var openedFiles = openedFilesText.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                OpenDocuments(openedFiles);

                if (this.mainTabControl.Items.Count <= 0)
                {
                    var newEditor = new MarkdownEditor("md_0001.md", this.IsExamEnabled);
                    this.mainTabControl.Items.Add(newEditor);
                    newEditor.EditorBase.Focus();
                }

                bool shouldFocusFstFile = true;
                //var editingFile = this.configManager.Get("EditingFile");
                //if (string.IsNullOrEmpty(editingFile) == false && File.Exists(editingFile))
                //{
                //    foreach (var item in this.mainTabControl.Items)
                //    {
                //        var me = item as MarkdownEditor;
                //        if (me == null) continue;

                //        if (me.FullFilePath == editingFile)
                //        {
                //            this.mainTabControl.SelectedItem = me;
                //            shouldFocusFstFile = false;
                //            break;
                //        }
                //    }
                //}

                var activeDocumentFullPath = this.WorkspaceConfigManager.Get("ActiveDocumentFullPath");
                if (File.Exists(activeDocumentFullPath))
                {
                    foreach (var item in this.mainTabControl.Items)
                    {
                        var me = item as MarkdownEditor;
                        if (me == null || me.FullFilePath == null) continue;

                        if (me.FullFilePath.ToLower() == activeDocumentFullPath.ToLower())
                        {
                            this.mainTabControl.SelectedItem = me;
                            shouldFocusFstFile = false;
                            break;
                        }
                    }
                }


                if (shouldFocusFstFile && this.mainTabControl.Items.Count > 0)
                {
                    this.mainTabControl.SelectedIndex = 0;
                }
            }

            if (string.IsNullOrWhiteSpace(rememberedActiveDocumentFullPath) == false &&
                File.Exists(rememberedActiveDocumentFullPath))
            {
                int index = -1;
                for (int i = 0; i < this.mainTabControl.Items.Count; i++)
                {
                    var editor = this.mainTabControl.Items[i] as MarkdownEditor;
                    if (editor == null) continue;

                    if (editor.FullFilePath == rememberedActiveDocumentFullPath)
                    {
                        index = i;
                        break;
                    }
                }

                if (index >= 0)
                {
                    this.mainTabControl.SelectedIndex = index;
                }
            }
        }

        /// <summary>
        /// 将新选中的工作区移动到历史工作区列表的头部。
        /// </summary>
        /// <param name="destWorkspaceFolder"></param>
        private static void WriteHistoryWorkspacesFile(string destWorkspaceFolder)
        {
            if (File.Exists(Globals.PathOfHistoryWorkspaceFileFullName))
            {
                string[] lines = File.ReadAllLines(Globals.PathOfHistoryWorkspaceFileFullName, new UnicodeEncoding());
                var newLinesList = new List<string>();
                foreach (var line in lines)
                {
                    if (line == destWorkspaceFolder) continue;

                    newLinesList.Add(line);
                }

                var newLines = new string[newLinesList.Count + 1];
                newLines[0] = destWorkspaceFolder;
                for (int i = 1; i < newLines.Length; i++)
                {
                    newLines[i] = newLinesList[i - 1];
                }

                File.WriteAllLines(Globals.PathOfHistoryWorkspaceFileFullName, newLines, new UnicodeEncoding());
            }
            else
            {
                File.WriteAllLines(Globals.PathOfHistoryWorkspaceFileFullName, new string[] { destWorkspaceFolder }, new UnicodeEncoding());
            }
        }

        private void fontFamilyList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (fontFamilyList.SelectedItem == null) return;

            var item = fontFamilyList.SelectedItem as FontFamilyListItem;
            if (item == null) return;

            //foreach (var ti in mainTabControl.Items)
            //{
            //    MarkdownEditor me = ti as MarkdownEditor;
            //    if (ti == null) continue;

            //    me.FontFamily = item.FontFamily;
            //}

            //右工具栏中的对照区不能直接对右工具栏变化字号
            foreach (var ue in this.rightToolBar.Items)
            {
                MarkdownEditor eti = ue as MarkdownEditor;
                if (eti == null) continue;

                eti.EditorBase.FontFamily = item.FontFamily;
            }

            this.mainTabControl.FontFamily = item.FontFamily;

            string fontEnName;
            if (item.FontFamily.FamilyNames.TryGetValue(System.Windows.Markup.XmlLanguage.GetLanguage("en-us"), out fontEnName))
            {
                this.configManager.Set("FontFamily", fontEnName);
            }
        }

        private void miHelp_Click(object sender, RoutedEventArgs e)
        {
            ShowHelp();
        }

        private void ShowHelp()
        {
            rightToolBar.SelectedIndex = 4;
            if (rightToolBar.Visibility != System.Windows.Visibility.Visible ||
                            rightToolBar.ActualWidth <= 0)
            {
                SwitchRightToolBarToggle();
            }
        }

        private void bdLeftToolsController_Click(object sender, RoutedEventArgs e)
        {
            SwitchLeftToolBarToggle();
        }

        private void SwitchLeftToolBarToggle()
        {
            if (leftToolBar.Visibility == System.Windows.Visibility.Visible)
            {
                leftToolBar.Visibility = System.Windows.Visibility.Collapsed;
                cdLeftToolsArea.MinWidth = 0;
                cdLeftToolsArea.Width = new GridLength(0);
            }
            else
            {
                leftToolBar.Visibility = System.Windows.Visibility.Visible;
                cdLeftToolsArea.MinWidth = 240;
                cdLeftToolsArea.Width = new GridLength(2, GridUnitType.Star);
                if (rightToolBar.Visibility == Visibility.Visible)
                {
                    cdRightToolsArea.Width =
                        cdMainEditArea.Width = new GridLength(3, GridUnitType.Star);
                }
                else
                {
                    cdMainEditArea.Width = new GridLength(6, GridUnitType.Star);
                }
            }
        }

        internal void SwitchRightToolBarToggle()
        {
            if (rightToolBar.Visibility == System.Windows.Visibility.Visible)
            {
                rightToolBar.Visibility = System.Windows.Visibility.Collapsed;
                cdRightToolsArea.MinWidth = 0;
                cdRightToolsArea.Width = new GridLength(0);
            }
            else
            {
                rightToolBar.Visibility = System.Windows.Visibility.Visible;
                cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
                if (leftToolBar.Visibility == Visibility.Visible)
                {
                    cdLeftToolsArea.Width = new GridLength(2, GridUnitType.Star);
                    cdMainEditArea.Width = new GridLength(3, GridUnitType.Star);
                }
                else
                {
                    cdMainEditArea.Width = new GridLength(5, GridUnitType.Star);
                }
            }
        }

        private void bdRightToolsController_Click(object sender, RoutedEventArgs e)
        {
            SwitchRightToolBarToggle();
        }

        private void miFormatBeforeSave_Click(object sender, RoutedEventArgs e)
        {
            this.miFormatBeforeSave.IsChecked = !this.miFormatBeforeSave.IsChecked;
            this.configManager.Set("AutoFormat", miFormatBeforeSave.IsChecked.ToString());
        }

        private bool rememberOpenedFiles = false;

        public bool RememberOpenedFiles
        {
            get { return rememberOpenedFiles; }
            set { rememberOpenedFiles = value; }
        }

        private void miRememberOpenedFiles_Click(object sender, RoutedEventArgs e)
        {
            miRememberOpenedFiles.IsChecked = !miRememberOpenedFiles.IsChecked;
            this.RememberOpenedFiles = miRememberOpenedFiles.IsChecked;
            this.configManager.Set("RememberOpenedFiles", miRememberOpenedFiles.IsChecked.ToString());
        }

        private void btnHelp_Click(object sender, RoutedEventArgs e)
        {
            ShowHelp();
        }

        private bool selectCellFirst = false;

        public bool SelectCellFirst
        {
            get { return selectCellFirst; }
            set { selectCellFirst = value; }
        }

        private bool collapseH1WhenCompile = false;

        public bool CollapseH1WhenCompile
        {
            get { return collapseH1WhenCompile; }
            set { collapseH1WhenCompile = value; }
        }

        private bool compileCodeToFillBlank = false;

        public bool CompileCodeToFillBlank
        {
            get { return compileCodeToFillBlank; }
            set { compileCodeToFillBlank = value; }
        }

        private bool hideChoiceQuestionAnswer = true;

        public bool HideChoiceQuestionAnswer
        {
            get { return hideChoiceQuestionAnswer; }
            set { hideChoiceQuestionAnswer = value; }
        }

        private bool textAutoWrap = true;

        public bool TextAutoWrap
        {
            get { return this.textAutoWrap; }
            set
            {
                this.textAutoWrap = value;
                RefreshTextAutoWrap();
            }
        }

        private void RefreshTextAutoWrap()
        {
            foreach (var item in this.mainTabControl.Items)
            {
                var editor = item as MarkdownEditor;
                if (editor == null) continue;

                editor.EditorBase.WordWrap = this.textAutoWrap;
            }
        }

        private bool isExamEnabled = true;

        public bool IsExamEnabled
        {
            get
            {
                return isExamEnabled;
            }

            set
            {
                isExamEnabled = value;
                foreach (var i in mainTabControl.Items)
                {
                    var me = i as MarkdownEditor;
                    if (me == null) continue;

                    me.IsExamEnabled = this.isExamEnabled;
                }

                if (isExamEnabled)
                {
                    mExams.Visibility = Visibility.Visible;
                    tabValidateDocument.Visibility = Visibility.Visible;
                }
                else
                {
                    mExams.Visibility = Visibility.Collapsed;
                    tabValidateDocument.Visibility = Visibility.Collapsed;
                }
            }
        }

        private void miSelectCellFirst_Click(object sender, RoutedEventArgs e)
        {
            miSelectCellFirst.IsChecked = !miSelectCellFirst.IsChecked;
            this.SelectCellFirst = miSelectCellFirst.IsChecked;
            this.configManager.Set("SelectCellFirst", miSelectCellFirst.IsChecked.ToString());
        }

        private void miCollapseH1TitlesWhenCompile_Click(object sender, RoutedEventArgs e)
        {
            miCollapseH1TitlesWhenCompile.IsChecked = !miCollapseH1TitlesWhenCompile.IsChecked;
            this.CollapseH1WhenCompile = miCollapseH1TitlesWhenCompile.IsChecked;
            this.configManager.Set("CollapseH1WhenCompile", miCollapseH1TitlesWhenCompile.IsChecked.ToString());
        }

        private void btnSetWorkspace_Click(object sender, RoutedEventArgs e)
        {
            ChangeWorkspace();
        }

        #region 搜索资源
        private void tbxSearchResource_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                SearchCustomResources();
                e.Handled = true;
            }
        }

        private void SearchCustomResources()
        {
            searchedResourceTreeviewItemList.Clear();
            searchResourceIndex = -1;

            this.cmbSearchResource.Items.Insert(0, cmbSearchResource.Text);

            if (SearchCustomResources(tvWorkDirectory.Items[0] as WorkspaceTreeViewItem, this.cmbSearchResource.Text))
            {
                if (searchedResourceTreeviewItemList.Count >= 0)
                {
                    searchResourceIndex += 1;
                    searchedResourceTreeviewItemList[searchResourceIndex].IsSelected = true;
                    ExpandWorkspaceTreeviewItem(searchedResourceTreeviewItemList[searchResourceIndex]);
                    tvWorkDirectory.Focus();
                }
            }

            if (tabControlLeftTools.SelectedIndex != 0)
                tabControlLeftTools.SelectedIndex = 0;
        }

        private bool SearchCustomResources(WorkspaceTreeViewItem item, string findKeyWord)
        {
            if (string.IsNullOrEmpty(findKeyWord)) return false;

            if (item == null) return false;

            bool finded = false;

            if (string.IsNullOrEmpty(item.ShortName)) return false;

            if (item.ShortName.ToLower().Contains(findKeyWord.ToLower()))
            {
                searchedResourceTreeviewItemList.Add(item);
                finded = true;
            }

            if (item.Items.Count > 0)
            {
                foreach (var it in item.Items)
                {
                    var subFined = SearchCustomResources(it as WorkspaceTreeViewItem, findKeyWord);
                    finded = subFined || finded;
                }
            }

            return finded;
        }

        private List<WorkspaceTreeViewItem> searchedResourceTreeviewItemList = new List<WorkspaceTreeViewItem>();

        private int searchResourceIndex = -1;

        private void btnSearchResource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SearchCustomResources();
        }

        private void btnSearchPreview_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectPreviewSearchedResource();
            e.Handled = true;//避免失焦
        }

        private void SelectPreviewSearchedResource()
        {
            if (searchResourceIndex > 0)
            {
                searchResourceIndex -= 1;
                if (searchResourceIndex >= 0 && searchResourceIndex < searchedResourceTreeviewItemList.Count)
                {
                    searchedResourceTreeviewItemList[searchResourceIndex].IsSelected = true;
                    ExpandWorkspaceTreeviewItem(searchedResourceTreeviewItemList[searchResourceIndex]);
                    tvWorkDirectory.Focus();
                }
            }
        }

        private void btnSearchNext_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SelectNextSearchedResource();
            e.Handled = true;//避免失焦
        }

        private void SelectNextSearchedResource()
        {
            if (searchResourceIndex < searchedResourceTreeviewItemList.Count - 1)
            {
                searchResourceIndex += 1;
                if (searchResourceIndex >= 0 && searchResourceIndex < searchedResourceTreeviewItemList.Count)
                {
                    searchedResourceTreeviewItemList[searchResourceIndex].IsSelected = true;
                    ExpandWorkspaceTreeviewItem(searchedResourceTreeviewItemList[searchResourceIndex]);
                    tvWorkDirectory.Focus();
                }
            }
        }

        private void ExpandWorkspaceTreeviewItem(WorkspaceTreeViewItem item)
        {
            if (item == null) return;

            item.IsExpanded = true;

            var parent = item.ParentWorkspaceTreeViewItem;
            while (parent != null)
            {
                parent.IsExpanded = true;
                parent = parent.ParentWorkspaceTreeViewItem;
            }
        }
        #endregion

        private void cmbFindText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (cmbSearchArea.SelectedIndex == 0)
                {
                    btnFind_Click(sender, e);
                }
                else
                {
                    FindText();
                }
            }
            //else if (e.Key == Key.Escape)
            //{
            //    btnCloseFindAndReplacePanel_Click(sender, e);
            //}
        }


        private void cmbReplaceTextInputBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (cmbSearchArea.SelectedIndex == 0)
                {
                    if ((Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0 ||
                        (Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0)
                    {
                        btnReplaceAll_Click(sender, e);
                    }
                    else
                    {
                        btnReplace_Click(sender, e);
                    }
                    e.Handled = true;
                }
            }
            //else if (e.Key == Key.Escape)
            //{
            //    btnCloseFindAndReplacePanel_Click(sender, e);
            //}
        }

        public void FindText()
        {
            tvFindAndReplace.Items.Clear();
            FindText(cmbFindText.Text, (cmbSearchArea.SelectedItem as ComboBoxItem).Tag.ToString());

            if (tvFindAndReplace.Items.Count <= 0)
            {
                tvFindAndReplace.Items.Add(new TreeViewItem() { Header = "<没找到结果...>", });
            }

            cmbFindText.Items.Add(cmbFindText.Text);

            if (rightToolBar.SelectedIndex != 1)
            {
                rightToolBar.SelectedIndex = 1;
            }

            if (cdRightToolsArea.ActualWidth < 140)
            {
                cdLeftToolsArea.Width = new GridLength(140);
            }
        }

        /// <summary>
        /// 在磁盘文档或当前正在编辑的文档之间查找指定文本内容。
        /// </summary>
        /// <param name="destText">如果指定了此参数，则按此参数查找。</param>
        /// <param name="preChar">前导字符，决定找到指定的文本后，双击后要选定的文本区域要向前移动到哪个字符。</param>
        /// <param name="nextChar">后缀字符，决定找到指定的文本后，双击后要选定的文本区域要向后移动到哪个字符。</param>
        private void FindText(string keyWord, string searchArea, char? preChar = null, char? nextChar = null)
        {
            if (string.IsNullOrEmpty(keyWord)) return;

            tvFindAndReplace.Items.Clear();

            int keywordLength = keyWord.Length;

            var type = FindLineTreeViewItem.ItemType.Normal;
            if (keyWord == "](@")
            {
                type = FindLineTreeViewItem.ItemType.Anchor;
            }

            switch (searchArea)
            {
                case "ActiveDocument":
                    {
                        var efi = this.mainTabControl.SelectedItem as MarkdownEditor;
                        if (efi != null)
                        {
                            FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(efi.FullFilePath, efi.ShortFileName);
                            var lines = efi.EditorBase.Document.Lines;
                            foreach (var line in lines)
                            {
                                var lineText = efi.EditorBase.Document.GetText(line.Offset, line.Length);
                                if (string.IsNullOrEmpty(lineText)) continue;
                                if (lineText.StartsWith("    ") || lineText.StartsWith("\t")) continue;//代码块中定义的锚不应该起作用。

                                var index = lineText.IndexOf(keyWord);
                                if (index >= 0)
                                {
                                    int preIndex = -1;
                                    int nextIndex = -1;
                                    if (preChar != null && preChar.HasValue)
                                    {
                                        preIndex = lineText.LastIndexOf(preChar.Value, Math.Max(index - 1, 0));
                                    }

                                    if (nextChar != null && nextChar.HasValue)
                                    {
                                        nextIndex = lineText.IndexOf(nextChar.Value, index + keywordLength);
                                    }

                                    if (preIndex >= 0 && nextIndex >= 0)
                                    {
                                        fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, preIndex, nextIndex - preIndex + 1, lineText, null,
                                            type));
                                    }
                                    else
                                    {
                                        fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, index, keywordLength, lineText, null,
                                             type));
                                    }
                                }
                            }
                            if (fdi.Items.Count > 0)
                            {
                                this.tvFindAndReplace.Items.Add(fdi);
                                fdi.IsExpanded = true;
                                (fdi.Items[0] as TreeViewItem).IsSelected = true;
                            }
                        }
                        break;
                    }
                case "OpenedDocuments":
                    {
                        foreach (var item in this.mainTabControl.Items)
                        {
                            var efi = item as MarkdownEditor;
                            if (efi != null)
                            {
                                FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(efi.FullFilePath, efi.ShortFileName);
                                var lines = efi.EditorBase.Document.Lines;
                                foreach (var line in lines)
                                {
                                    var lineText = efi.EditorBase.Document.GetText(line.Offset, line.Length);
                                    if (string.IsNullOrEmpty(lineText)) continue;
                                    if (lineText.StartsWith("    ") || lineText.StartsWith("\t")) continue;//代码块中定义的锚不应该起作用。

                                    var index = lineText.IndexOf(keyWord);
                                    if (index >= 0)
                                    {
                                        int preIndex = -1;
                                        int nextIndex = -1;
                                        if (preChar != null && preChar.HasValue)
                                        {
                                            preIndex = lineText.LastIndexOf(preChar.Value, Math.Max(index - 1, 0));
                                        }

                                        if (nextChar != null && nextChar.HasValue)
                                        {
                                            nextIndex = lineText.IndexOf(nextChar.Value, index + keywordLength);
                                        }

                                        if (preIndex >= 0 && nextIndex >= 0)
                                        {
                                            fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, preIndex, nextIndex - preIndex + 1, lineText, null,
                                                 type));
                                        }
                                        else
                                        {
                                            fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, index, keywordLength, lineText, null,
                                                 type));
                                        }
                                    }
                                }
                                if (fdi.Items.Count > 0)
                                {
                                    this.tvFindAndReplace.Items.Add(fdi);
                                    fdi.IsExpanded = true;
                                    (fdi.Items[0] as TreeViewItem).IsSelected = true;
                                }
                            }
                        }
                        break;
                    }
                case "AllFiles":
                    {
                        //这个需要用到递归
                        FindTextInAllFiles(Globals.PathOfWorkspace, keyWord, preChar, nextChar);
                        break;
                    }
            }
        }

        private void FindTextInAllFiles(string directoryPath, string keyWord, char? preChar, char? nextChar)
        {
            if (Directory.Exists(directoryPath) == false) return;
            if (string.IsNullOrEmpty(keyWord)) return;

            var keywordLength = keyWord.Length;

            var type = FindLineTreeViewItem.ItemType.Normal;
            if (keyWord == "](@")
            {
                type = FindLineTreeViewItem.ItemType.Anchor;
            }

            var directory = new DirectoryInfo(directoryPath);

            //先处理当前目录下的文件
            var childFilesInfos = directory.GetFiles();

            foreach (var childFileInfo in childFilesInfos)
            {
                if (childFileInfo.FullName.ToLower().EndsWith(".md") == false) continue;

                string[] lines;

                //已打开的文件，按打开的情况算，没打开的，按磁盘文本查找。
                var fileEditor = GetOpenedEditor(childFileInfo.FullName);
                if (fileEditor != null)
                {
                    lines = fileEditor.EditorBase.Text.Replace("\r", "").Split(new char[] { '\n' });
                }
                else
                {
                    lines = File.ReadAllLines(childFileInfo.FullName);
                }

                FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(childFileInfo.FullName, childFileInfo.Name);

                var lineNum = 0;
                foreach (var lineText in lines)
                {
                    lineNum++;

                    if (lineText.StartsWith("    ") || lineText.StartsWith("\t")) continue;//代码块中定义的锚不应该起作用。

                    var index = lineText.IndexOf(keyWord);
                    if (index >= 0)
                    {
                        int preIndex = -1;
                        int nextIndex = -1;
                        if (preChar != null && preChar.HasValue)
                        {
                            if (index >= 1)
                            {
                                preIndex = lineText.LastIndexOf(preChar.Value, index - 1);
                            }
                            else
                            {
                                preIndex = 0;
                            }
                        }

                        if (nextChar != null && nextChar.HasValue)
                        {
                            nextIndex = lineText.IndexOf(nextChar.Value, index + keywordLength);
                        }

                        if (preIndex >= 0 && nextIndex >= 0)
                        {
                            fdi.Items.Add(new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name, lineNum, preIndex, nextIndex - preIndex - 1, lineText, null,
                                 type));
                            //fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, line.LineNumber, preIndex, nextIndex - preIndex, lineText));
                        }
                        else
                        {
                            fdi.Items.Add(new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name, lineNum, index, keywordLength, lineText, null,
                                 type));
                            //fdi.Items.Add(new FindLineTreeViewItem(efi.FullFilePath, line.LineNumber, index, keywordLength, lineText));
                        }
                    }

                }

                if (fdi.Items.Count > 0)
                {
                    this.tvFindAndReplace.Items.Add(fdi);
                    fdi.IsExpanded = true;
                }
            }

            //再递归处理子目录下的文件
            var childDirectories = directory.GetDirectories();
            foreach (var childDirectory in childDirectories)
            {
                FindTextInAllFiles(childDirectory.FullName, keyWord, preChar, nextChar);
            }
        }

        private void btnFindText_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            FindText();
        }

        //private void Image_MouseEnter(object sender, MouseEventArgs e)
        //{
        //    var parent = (sender as Image).Parent as Border;
        //    parent.Background = Brushes.AliceBlue;
        //}

        //private void Image_MouseLeave(object sender, MouseEventArgs e)
        //{
        //    var parent = (sender as Image).Parent as Border;
        //    parent.Background = Brushes.Transparent;
        //}

        private void btnSetWorkspace_MouseEnter(object sender, MouseEventArgs e)
        {
            ((sender as TextBlock).Parent as Border).Background = Brushes.LightBlue;
        }

        private void btnSetWorkspace_MouseLeave(object sender, MouseEventArgs e)
        {
            ((sender as TextBlock).Parent as Border).Background = Brushes.Transparent;
        }

        private void vbSearchResource_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.LightBlue;
        }

        private void vbSearchResource_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.Transparent;
        }

        private void vbPreviewFindedResource_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.LightBlue;
        }

        private void vbPreviewFindedResource_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.Transparent;
        }

        private void vbNextFindedResource_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.LightBlue;
        }

        private void vbNextFindedResource_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.Transparent;
        }

        private void vbFindText_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.LightBlue;
        }

        private void vbFindText_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.Transparent;
        }

        private void miFoldThisBlock_Click(object sender, RoutedEventArgs e)
        {
            var efi = mainTabControl.SelectedItem as MarkdownEditor;
            if (efi == null) return;

            efi.EditorBase.FoldSelectedBlock();
        }

        private void miFoldingAll_Click(object sender, RoutedEventArgs e)
        {
            var efi = mainTabControl.SelectedItem as MarkdownEditor;
            if (efi == null) return;

            foreach (var i in efi.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }
        }

        private void miUnFoldingAll_Click(object sender, RoutedEventArgs e)
        {
            var efi = mainTabControl.SelectedItem as MarkdownEditor;
            if (efi == null) return;

            foreach (var i in efi.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }
        }

        private void miOutportWebSite_Click(object sender, RoutedEventArgs e)
        {
            string destOutportFolder;

            //选择新工作目录
            System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog()
            {
                Description = "　　请选择一个文件作为导出目标。\r\n　　导出时会简单地将除Markdown文件以外的所有文件复制到目标文件夹中。",
                ShowNewFolderButton = true,
            };
            System.Windows.Interop.HwndSource source = PresentationSource.FromVisual(this) as System.Windows.Interop.HwndSource;

            System.Windows.Forms.IWin32Window win = new WinFormWindow(source.Handle);
            System.Windows.Forms.DialogResult result = fbd.ShowDialog(win);
            if (result.Equals(System.Windows.Forms.DialogResult.OK))
            {
                destOutportFolder = fbd.SelectedPath;
            }
            else return;

            OutportFiles(destOutportFolder);
        }

        private void OutportFiles(string destOutportFolder)
        {
            if (Directory.Exists(destOutportFolder) == false)
            {
                LMessageBox.Show("　　指定目录不存在！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (destOutportFolder[destOutportFolder.Length - 1] != Path.DirectorySeparatorChar)
            {
                destOutportFolder += Path.DirectorySeparatorChar;
            }

            try
            {
                bool compileBeforeOutport = false;

                var result = LMessageBox.Show("　　导出前要重新编译所有Markdown文件吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    compileBeforeOutport = true;
                }

                List<CompiledFileInfo> compiledFilePaths = new List<CompiledFileInfo>();

                CopyCompiledDirectory(Globals.PathOfWorkspace, destOutportFolder, compileBeforeOutport, ref compiledFilePaths);

                //生成一个html索引文档。
                var indexFilePath = BuildIndexOfCompiledHtmlFiles(compiledFilePaths);
                if (File.Exists(destOutportFolder + "_Index.html") == false)
                {
                    File.Copy(indexFilePath, destOutportFolder + "_Index.html");
                }

                // var lines = File.ReadAllLines(Globals.PathOfHistoryWorkspaceFileFullName);
                if (File.Exists(Globals.PathOfHistoryOutputFileFullName))
                {
                    string[] lines = File.ReadAllLines(Globals.PathOfHistoryOutputFileFullName, new UnicodeEncoding());
                    var newLinesList = new List<string>();
                    foreach (var line in lines)
                    {
                        if (line == destOutportFolder) continue;

                        newLinesList.Add(line);
                    }

                    var newLines = new string[newLinesList.Count + 1];
                    newLines[0] = destOutportFolder;
                    for (int i = 1; i < newLines.Length; i++)
                    {
                        newLines[i] = newLinesList[i - 1];
                    }

                    File.WriteAllLines(Globals.PathOfHistoryOutputFileFullName, newLines, new UnicodeEncoding());
                }
                else
                {
                    File.WriteAllLines(Globals.PathOfHistoryOutputFileFullName, new string[] { destOutportFolder }, new UnicodeEncoding());
                }

                for (int i = lbxHistoryOutport.Items.Count - 1; i >= 0; i--)
                {
                    var rwitem = lbxHistoryOutport.Items[i] as RecentDirectoryListBoxItem;
                    if (rwitem != null && rwitem.DirectoryPath == destOutportFolder)
                    {
                        lbxHistoryOutport.Items.Remove(rwitem);
                    }
                }

                var row = new RecentDirectoryListBoxItem(destOutportFolder);
                row.MouseDoubleClick += row_MouseDoubleClick;

                lbxHistoryOutport.Items.Insert(0, row);
                lbxHistoryOutport.SelectedIndex = 0;

                tabControlLeftTools.SelectedIndex = 0;
                RefreshOutputHistoryList();

                //LMessageBox.Show("　　已将工作区目录下所有非Markdown文件（目录）导出到以下磁盘位置：\r\n　　" +
                //    destOutportFolder, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);

                System.Diagnostics.Process.Start("explorer.exe", destOutportFolder);
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void RefreshOutputHistoryList()
        {
            for (int i = 0; i < lbxHistoryOutport.Items.Count; i++)
            {
                var item = lbxHistoryOutport.Items[i] as RecentDirectoryListBoxItem;
                if (item == null) continue;

                item.IndexText = (i + 1).ToString();
            }
        }

        private string BuildIndexOfCompiledHtmlFiles(List<CompiledFileInfo> compiledFilePaths)
        {
            if (compiledFilePaths.Count > 0)
            {
                StringBuilder sbIndex = new StringBuilder();
                sbIndex.Append("<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n"
                    + "<meta name=\"viewport\" content=\"width = device - width, user - scalable = yes\" />\n"
                    + "<meta http-equiv=\"Content - Type\" content=\"text / html; charset = utf - 8\">\n"
                    + "<title>Markdown Documents Index</title>\n<link rel=\"stylesheet\" href=\"./lesson_" + Globals.MainWindow.ColorText + ".css\" type=\"text/css\"></head>\n<body>\n");

                int tmpIndex = Globals.PathOfWorkspace.Length;
                string preDirectory = "/";
                foreach (CompiledFileInfo cfi in compiledFilePaths)
                {
                    string prefix;
                    if (cfi.FileFullName.StartsWith("directory_entry:///"))
                    {
                        //目录
                        preDirectory = cfi.FileFullName.Substring(19).Substring(Globals.PathOfWorkspace.Length).Replace("\\", "/");
                        if (preDirectory.EndsWith("/") == false) preDirectory += "/";

                        prefix = BuildPrefix(preDirectory, true);
                        sbIndex.Append("<p>" + prefix + preDirectory.Replace("/", "&nbsp;") + " </p>");
                        continue;
                    }

                    //html文件
                    string tmpPath = cfi.FileFullName;
                    if (cfi.FileFullName.StartsWith(Globals.PathOfWorkspace))
                    {
                        tmpPath = /*destPath + */ cfi.FileFullName.Substring(tmpIndex).Replace("\\", "/").Substring(preDirectory.Length - 1);
                        if (tmpPath.StartsWith("/")) tmpPath = tmpPath.Substring(1);
                    }
                    prefix = BuildPrefix(preDirectory, false);
                    var text = cfi.Title;
                    if (string.IsNullOrEmpty(text))
                    {
                        text = (tmpPath.EndsWith(".html") ? tmpPath.Substring(0, tmpPath.Length - 5) : tmpPath);
                    }
                    sbIndex.Append("<p>" + prefix + "<a href=\"./" +
                        (preDirectory == "/" ? "" : preDirectory) + tmpPath + "\">" + text + "</a></p>\n");
                }

                sbIndex.Append("</body>\n</html>");

                var indexHtmlFilePath = Globals.PathOfWorkspace + "_index.html";

                File.WriteAllText(indexHtmlFilePath, sbIndex.ToString(), Encoding.UTF8);
                return indexHtmlFilePath;
            }
            else return "";
        }

        private string BuildPrefix(string preDirectory, bool isfolder)
        {
            if (string.IsNullOrEmpty(preDirectory)) return string.Empty;

            StringBuilder sb = new StringBuilder();
            foreach (char c in preDirectory)
            {
                if (c == '/' || c == '\\') sb.Append("　");
            }

            if (isfolder) return sb.ToString();
            else return sb.Append("　").ToString();
        }

        void row_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            OutportFiles((sender as RecentDirectoryListBoxItem).DirectoryPath);
        }

        public static void CompileAllMdFiles(ref List<CompiledFileInfo> compiledFiles)
        {
            CopyCompiledDirectory(Globals.PathOfWorkspace, "", true, ref compiledFiles);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcDirectory"></param>
        /// <param name="destDirectory">传入无效的路径可以避免复制，这样就只是编译MD文件了。</param>
        /// <param name="compileMarkdownFilesBeforeCopy"></param>
        /// <param name="compiledFilePaths"></param>
        public static void CopyCompiledDirectory(string srcDirectory, string destDirectory, bool compileMarkdownFilesBeforeCopy, ref List<CompiledFileInfo> compiledFilePaths)
        {
            String[] Entries;

            if (string.IsNullOrEmpty(destDirectory) == false && destDirectory[destDirectory.Length - 1] != Path.DirectorySeparatorChar)
            {
                destDirectory += Path.DirectorySeparatorChar;
            }

            //不能用下面这段代码，因为destDirectory完全可能不是合法的目录路径，这样会引发异常。
            //if (string.IsNullOrEmpty(destDirectory) == false && !Directory.Exists(destDirectory))
            //{
            //    try
            //    {
            //        Directory.CreateDirectory(destDirectory);
            //    }
            //    catch (Exception ex)
            //    {
            //        LMessageBox.Show(ex.Message);
            //        return;
            //    }
            //}

            Entries = Directory.GetFileSystemEntries(srcDirectory);

            if (compileMarkdownFilesBeforeCopy)
            {
                foreach (string fileEntry in Entries)
                {
                    //必须分两次循环，先考虑编译，再复制
                    if (File.Exists(fileEntry))
                    {
                        var srcFilePath = fileEntry;// srcDirectory + Path.GetFileName(fileEntry);
                        if (srcFilePath.ToLower().EndsWith(".md"))
                        {
                            string htmlTitle = "";

                            if (Globals.MainWindow.IgnoreEncryptedFiles)
                            {
                                string password, passwordTip;
                                var isEncrypted = CustomMarkdownSupport.IsFileEncrypted(srcFilePath, out password, out passwordTip);
                                if (isEncrypted) continue;//忽略被加密的文档。
                            }

                            var filePath = CustomMarkdownSupport.Compile(srcFilePath,
                                        Globals.MainWindow.CollapseH1WhenCompile, ref htmlTitle);
                            if (filePath == null) continue;

                            compiledFilePaths.Add(
                                new CompiledFileInfo()
                                {
                                    FileFullName = filePath,
                                    Title = htmlTitle,
                                });
                        }
                    }
                }
            }

            foreach (string directoryEntry in Entries)
            {
                if (Directory.Exists(directoryEntry))
                {
                    if (directoryEntry.EndsWith("~") == false && directoryEntry.EndsWith("~" + Path.DirectorySeparatorChar) == false)
                    {
                        compiledFilePaths.Add(new CompiledFileInfo()
                        {
                            FileFullName = "directory_entry:///" + directoryEntry,
                            Title = "",
                        });
                    }

                    if (string.IsNullOrWhiteSpace(destDirectory))
                    {
                        CopyCompiledDirectory(directoryEntry, "", compileMarkdownFilesBeforeCopy, ref compiledFilePaths);
                    }
                    else {
                        CopyCompiledDirectory(directoryEntry, destDirectory + Path.GetFileName(directoryEntry), compileMarkdownFilesBeforeCopy, ref compiledFilePaths);
                    }
                }
                else
                {
                    var destFileFullPath = destDirectory + Path.GetFileName(directoryEntry);
                    if (string.IsNullOrWhiteSpace(destDirectory) == false)//为空字符串时不复制。
                    {
                        if (Directory.Exists(destDirectory) == false && string.IsNullOrWhiteSpace(destDirectory) == false)
                        {
                            try
                            {
                                Directory.CreateDirectory(destDirectory);

                                if (destFileFullPath.ToLower().EndsWith(".md") == false && File.Exists(destFileFullPath) == false)
                                {
                                    System.IO.File.Copy(directoryEntry, destFileFullPath, true);
                                }
                            }
                            catch (Exception ex)
                            {
                                LMessageBox.Show(ex.Message + "\r\n" + ex.StackTrace);
                            }
                        }
                        else
                        {

                            if (destFileFullPath.ToLower().EndsWith(".md") == false && File.Exists(destFileFullPath) == false)
                            {
                                System.IO.File.Copy(directoryEntry, destFileFullPath, true);
                            }
                        }
                    }
                }
            }
        }

        private void btnCompileAndPreviewHtml_MouseLeftButtonDown(object sender, RoutedEventArgs e)
        {
            CompileAndPreviewHtml(true);
        }

        private void miInsertHorizontalLine_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            if (mi == null) return;

            var value = int.Parse(mi.Tag as string);

            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.SelectedText = "<hr class=\"hr" + value + "\"/>\r\n";
            eti.EditorBase.Select(eti.EditorBase.SelectionStart + eti.EditorBase.SelectionLength, 0);
        }

        private void miWrapWithSquareQuoters_Click(object sender, RoutedEventArgs e)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;
            eti.EditorBase.WrapTextWithSquareQuotes();
        }

        private void miInsertChoiceQuestion_Click(object sender, RoutedEventArgs e)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;
            eti.EditorBase.InsertChoiceQuestionTags();
        }

        private void miInsertJudgeQuestion_Click(object sender, RoutedEventArgs e)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;
            eti.EditorBase.InsertJudgeQuestionTags((sender as MenuItem).Tag.ToString());
        }

        private void miValidateTestPaper_Click(object sender, RoutedEventArgs e)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            StringBuilder errorMsg = new StringBuilder();
            if (QuestionValidateManager.ValidateAsTestPaper(OutLineExamsText(eti.EditorBase.Document.Text), errorMsg))
            {
                tbxValidatedInfo.Text = "文件通过合法性验证，可以使用。\r\n==========√√√√√==========\r\n";
            }
            else
            {
                tbxValidatedInfo.Text = "文件未通过合法性验证，错误消息如下：\r\n============××××××============\r\n" + errorMsg.ToString();
            }

            if (rightToolBar.SelectedItem != tabValidateDocument)
            {
                rightToolBar.SelectedItem = tabValidateDocument;
            }
        }

        private void miPresentation_Click(object sender, RoutedEventArgs e)
        {
            PresentationExams();
        }

        private string OutLineExamsText(string src)
        {
            if (string.IsNullOrEmpty(src)) return "";

            var lines = src.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder sb = new StringBuilder();
            foreach (var l in lines)
            {
                if (Question.IsExamTextLine(l))
                {
                    sb.Append(l);
                    sb.Append("\r\n");
                }
            }

            return sb.ToString();
        }

        public void PresentationExams()
        {
            var edit = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (edit == null)
            {
                LMessageBox.Show("没有文档可以显示！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var examsText = OutLineExamsText(edit.EditorBase.Document.Text);

            StringBuilder errorMsg = new StringBuilder();
            if (QuestionValidateManager.ValidateForPresentation(examsText, errorMsg))//这包括了填空题。
            {
                tbxValidatedInfo.Text = "文件通过合法性验证，可以使用。\r\n========√√√√========\r\n";
            }
            else
            {
                tbxValidatedInfo.Text = "文件未通过合法性验证，错误消息如下：\r\n=========××××========\r\n" + errorMsg.ToString();

                if (rightToolBar.SelectedItem != tabValidateDocument)
                {
                    rightToolBar.SelectedItem = tabValidateDocument;
                }

                LMessageBox.Show("　　此文档中无试题或试题有格式错误，无法演示！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);

                return;
            }

            //这句不能少，否则头一次显示不出任何试题。
            edit.PresentationWindow.BuildQuestions(examsText);
            edit.PresentationWindow.ShowDialog();
        }

        private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                CompileAndPreviewHtml(true);
            }
        }

        private void TextBlock_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as TextBlock).Background = Brushes.AliceBlue;
        }

        private void TextBlock_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as TextBlock).Background = Brushes.Transparent;
        }

        private void miCreateStudentsList_Click(object sender, RoutedEventArgs e)
        {
            var inputBoxResult = InputBox.Show(Globals.AppName, "　　请输入班级名：", "", false);

            if (string.IsNullOrEmpty(inputBoxResult)) return;

            try
            {
                string studentListDirectoryPath = Globals.PathOfUserFolder + (Globals.PathOfUserFolder.EndsWith("\\") ? "" : "\\") + "StudentsLists\\";
                if (Directory.Exists(studentListDirectoryPath) == false)
                {
                    Directory.CreateDirectory(studentListDirectoryPath);
                }

                DirectoryInfo studentListDirectoryinfo = new DirectoryInfo(studentListDirectoryPath);

                var newStudentsListFilePath = studentListDirectoryPath + (inputBoxResult.EndsWith(".txt") ? inputBoxResult : (inputBoxResult + ".txt"));
                if (File.Exists(newStudentsListFilePath) == false)
                {
                    //或不存在同名文件，就创建一个。
                    File.WriteAllText(newStudentsListFilePath, Properties.Resources.SampleClassFileContent);
                }

                //编辑文件
                var sw = new StudentsListEditor(newStudentsListFilePath)
                {
                    Owner = this,
                    WindowStartupLocation = WindowStartupLocation.CenterOwner,
                };
                sw.ShowDialog();
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message);
            }
        }

        private void miIsExamEnabled_Click(object sender, RoutedEventArgs e)
        {
            miIsExamEnabled.IsChecked = !miIsExamEnabled.IsChecked;
            this.IsExamEnabled = miIsExamEnabled.IsChecked;
            this.configManager.Set("IsExamEnabled", miIsExamEnabled.IsChecked.ToString());
        }

        private void imagePreviewOutBorder_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                var wti = (sender as Border).Tag as WorkspaceTreeViewItem;
                if (wti == null) return;

                wti.IsSelected = true;
            }
        }

        private void btnFindAnchors_MouseLeftButtonDown(object sender, RoutedEventArgs e)
        {
            //锚的定义方式：
            //[锚名](@锚ID)
            //锚名可以省略，但锚ID不能省略。
            //编译后，会变成这样：
            //<a id="锚ID">锚名</a>
            FindText("](@", (cmbSearchArea.SelectedItem as ComboBoxItem).Tag.ToString(), '[', ')');

            if (rightToolBar.SelectedIndex != 1)
            {
                rightToolBar.SelectedIndex = 1;
            }

            if (cdRightToolsArea.ActualWidth < 140)
            {
                cdLeftToolsArea.Width = new GridLength(140);
            }
        }

        public MarkdownEditor ActivedEditor
        {
            get
            {
                if (this.mainTabControl.Items.Count <= 0) return null;

                return this.mainTabControl.SelectedItem as MarkdownEditor;
            }
        }

        public bool StartFullScreen { get; private set; }

        private void miFindRefInActiveDocument_Click(object sender, RoutedEventArgs e)
        {
            FindRef("ActiveDocument");
        }

        private void FindRef(string findArea)
        {
            var wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi == null) return;

            var shortName = wtvi.ShortName;
            if (shortName.ToLower().EndsWith(".md"))
            {
                shortName = shortName.Substring(0, shortName.Length - 3);
            }

            if (string.IsNullOrEmpty(shortName)) return;

            FindText(shortName, findArea);

            rightToolBar.SelectedItem = tiReplace;
        }

        private void miFindRefInOpenedDocument_Click(object sender, RoutedEventArgs e)
        {
            FindRef("OpenedDocuments");
        }

        private void miFindRefInDiskFiles_Click(object sender, RoutedEventArgs e)
        {
            bool someFileIsModified = false;
            foreach (var i in this.mainTabControl.Items)
            {
                MarkdownEditor me = i as MarkdownEditor;
                if (me == null) continue;

                if (me.IsModified)
                {
                    someFileIsModified = true;
                    break;
                }
            }

            if (someFileIsModified)
            {
                var result = LMessageBox.Show("　　这个功能只管从磁盘上查找，而不管当前正在编辑的内容，所以查找的结果未必准确。\r\n" +
                    "　　如果要保证查找结果准确，请先保存这些文档或者使用“查找打开的文档”、“查找当前文档”这两个菜单条目！\r\n\r\n　　要继续吗？",
                    Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result != MessageBoxResult.Yes) return;
            }

            FindRef("AllFiles");
        }

        private void miItalic_Click(object sender, RoutedEventArgs e)
        {
            WrapWithItalic();
        }

        private void miBold_Click(object sender, RoutedEventArgs e)
        {
            WrapWithBold();
        }

        private void miUnderLine_Click(object sender, RoutedEventArgs e)
        {
            WrapWithUTag();
        }

        private void miStrikeLine_Click(object sender, RoutedEventArgs e)
        {
            WrapWithSTag();
        }

        private void miStartFullScreen_Click(object sender, RoutedEventArgs e)
        {
            miStartFullScreen.IsChecked = !miStartFullScreen.IsChecked;
            StartFullScreen = miStartFullScreen.IsChecked;
            configManager.Set("StartFullScreen", miStartFullScreen.IsChecked.ToString());
        }

        private void miFullScreen_Click(object sender, RoutedEventArgs e)
        {
            SwitchFullScreen(mainMenu.Visibility == Visibility.Visible);

            if (mainMenu.Visibility == Visibility.Visible)
            {
                miFullScreen.IsChecked =
                miFullScreenC.IsChecked = false;
            }
            else
            {
                miFullScreen.IsChecked =
                miFullScreenC.IsChecked = true;
            }
        }

        private void btnFullScreen_Click(object sender, RoutedEventArgs e)
        {
            SwitchFullScreen(true);
        }

        private void btnMinize_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.WindowState = WindowState.Minimized;
        }

        private void btnRestore_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (this.WindowState == WindowState.Normal)
            {
                this.WindowState = WindowState.Maximized;
            }
            else if (this.WindowState == WindowState.Maximized)
            {
                this.WindowState = WindowState.Normal;
            }
        }

        private void btnClose_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.Close();
        }

        private BitmapImage restoreImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/restore.png"));

        private BitmapImage maxsizeImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/maxsize.png"));

        private void miWriteControlTemplate_Click(object sender, RoutedEventArgs e)
        {
            string templateText = GetTemplateXamlCode(mainTabControl.SelectedItem as Control);
            File.WriteAllText("D:\\控件模板.txt", templateText);
            LMessageBox.Show("控件模板文件已输出到：D:\\控件模板.txt");
        }

        string GetTemplateXamlCode(Control ctrl)
        {
            FrameworkTemplate template = ctrl.Template;
            string xaml = "";
            if (template != null)
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.IndentChars = new string(' ', 4);
                settings.NewLineOnAttributes = true;
                StringBuilder strbuild = new StringBuilder();
                XmlWriter xmlwrite = XmlWriter.Create(strbuild, settings);
                try
                {
                    XamlWriter.Save(template, xmlwrite);
                    xaml = strbuild.ToString();
                }
                catch (Exception exc)
                {
                    xaml = exc.Message;
                }
            }
            else
            {
                xaml = "no template";
            }
            return xaml;
        }

        private void dpTitle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.DragMove();
            }

            if (e.ClickCount == 2)
            {
                if (this.WindowState == WindowState.Maximized)
                {
                    this.WindowState = WindowState.Normal;
                }
                else if (this.WindowState == WindowState.Normal)
                {
                    this.WindowState = WindowState.Maximized;
                }
            }
        }

        // 已修正
        private void FindChildByType(DependencyObject relate, Type type, ref FrameworkElement resElement)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(relate); i++)
            {
                var el = VisualTreeHelper.GetChild(relate, i) as FrameworkElement;
                if (el.GetType() == type)
                {
                    resElement = el;
                    return;
                }
                else
                {
                    FindChildByType(el, type, ref resElement);
                }
            }
        }

        private string defaultEncoding = "utf-8";
        /// <summary>
        /// 转换成的html的默认编码。
        /// </summary>
        public string DefaultEncoding
        {
            get
            {
                return defaultEncoding;
            }
        }

        private void miUtf8_Click(object sender, RoutedEventArgs e)
        {
            SelectEncoding(miUtf8.Tag.ToString());
        }

        private void miGb2312_Click(object sender, RoutedEventArgs e)
        {
            SelectEncoding(miGb2312.Tag.ToString());
        }

        private void SelectEncoding(string encoding)
        {
            RefreshEncoding(encoding);

            this.ConfigManager.Set("Encoding", encoding);
        }

        private void RefreshEncoding(string encoding)
        {
            if (string.IsNullOrEmpty(encoding))
            {
                encoding = "utf-8";
            }

            if (encoding == "gb2312")
            {
                defaultEncoding = "gb2312";
                miUtf8.IsChecked = false;
                miGb2312.IsChecked = true;
            }
            else//都强制为UTF-8
            {
                if (encoding != "utf-8")
                {
                    encoding = "utf-8";
                }

                miUtf8.IsChecked = true;
                miGb2312.IsChecked = false;
                defaultEncoding = "utf-8";
            }
        }

        private void miCreateCHMProject_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var prefixOfFiles = Globals.PathOfWorkspace + Globals.WorkspaceShortName;
                var chmProjectFilePath = prefixOfFiles + ".hhp";
                var chmContentFilePath = prefixOfFiles + ".hhc";
                var chmIndexFilePath = prefixOfFiles + ".hhk";

                //var indexHtmlPath = "index.html";

                if (File.Exists(chmProjectFilePath) == true)
                {
                    var result = LMessageBox.Show("　　★请慎用此功能！！！\r\n　　它会将你之前创建的所有工程文件全部重置！！！\r\n\r\n" +
                        "　　所以，除非你从来没有在这个工作区使用过此功能，或者根本不介意之前自己手工修改的成果被全部抹杀——千万别用这个功能！\r\n\r\n" +
                        "　　你真的要继续吗？", Globals.AppName,
                         MessageBoxButton.YesNo, MessageBoxImage.Warning);
                    if (result != MessageBoxResult.Yes) return;
                }

                //先编译所有Markdown文档。
                var result2 = LMessageBox.Show("　　在创建 CHM 工程文件之前，应按 GB2312 编码方式对工作区中所有 Markdown 文件进行编译，要自动进行这步操作吗？", Globals.AppName,
                     MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result2 == MessageBoxResult.Yes)
                {
                    miGb2312.IsChecked = true;
                    miGb2312_Click(sender, e);
                    CompileWholeWorkspace();
                }

                var gb2312 = Encoding.GetEncoding("gb2312");

                //第①步，生成hhp工程文件
                var sbProject = new StringBuilder();

                #region 工程文件内容

                sbProject.Append("[OPTIONS]" + "\r\n");
                sbProject.Append("Compatibility= 1.1 or later" + "\r\n");
                sbProject.Append("Compiled file = " + Globals.WorkspaceShortName + ".chm\r\n");
                sbProject.Append("Contents file = " + Globals.WorkspaceShortName + ".hhc\r\n");
                sbProject.Append("Default Font=新宋体,11,1\r\n");

                //这里还需要进一步润色。
                sbProject.Append("Default topic = _Index.html\r\n");

                sbProject.Append("Display compile progress = Yes" + "\r\n");
                sbProject.Append("Index file = " + Globals.WorkspaceShortName + ".hhk\r\n");
                sbProject.Append("Language = 0x804 中文(简体，中国)" + "\r\n");
                sbProject.Append("Title=" + Globals.WorkspaceShortName);

                sbProject.Append("\r\n\r\n");

                sbProject.Append("[FILES]" + "\r\n");
                sbProject.Append("_Index.html\r\n");
                sbProject.Append("Images~\\header_icon_dark.png\r\n");
                sbProject.Append("Images~\\header_icon_light.png\r\n");
                sbProject.Append("Images~\\comment_dark.png\r\n");
                sbProject.Append("Images~\\comment_light.png\r\n");

                sbProject.Append("\r\n");

                sbProject.Append("[INFOTYPES]" + "\r\n");

                #endregion

                File.WriteAllText(chmProjectFilePath, sbProject.ToString(), gb2312);

                //第②步，生成hhc目录文件
                var sbContent = new StringBuilder();

                #region 目录文件内容

                //目录文件头，包括首页（_Index.html）
                sbContent.Append("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\r\n");
                sbContent.Append("<HTML>\r\n");
                sbContent.Append("<HEAD>\r\n");
                sbContent.Append("<meta name=\"GENERATOR\" content=\"Microsoft&reg; HTML Help Workshop 4.1\">");
                sbContent.Append("<!--Sitemap 1.0-->\r\n");
                sbContent.Append("</HEAD><BODY>\r\n");
                sbContent.Append("<OBJECT type=\"text /site properties\">\r\n");
                sbContent.Append("\t<param name=\"ImageType\" value=\"Folder\">\r\n");
                sbContent.Append("</OBJECT>");

                //目录文件内容
                //如果是子目录，在子目录“< UL >”Tag前加上：
                //<LI> <OBJECT type="text/sitemap">
                //    < param name = "Name" value = "其它" >
                //    </ OBJECT >
                //有一层子目录就加一层“< UL >”Tag对

                var hasInvalidatedFilePath = CreateCHMContentFile(new DirectoryInfo(Globals.PathOfWorkspace), "", "", sbContent);

                //目录文件尾
                sbContent.Append("</BODY></HTML>\r\n");

                #endregion

                File.WriteAllText(chmContentFilePath, sbContent.ToString(), gb2312);

                //第③步，生成hhk索引文件
                var sbIndex = new StringBuilder();

                #region 索引文件内容

                //索引文件头，包括首页（_Index.html）
                sbIndex.Append("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\r\n");
                sbIndex.Append("<HTML>\r\n");
                sbIndex.Append("<HEAD>\r\n");
                sbIndex.Append("<meta name=\"GENERATOR\" content=\"Microsoft&reg; HTML Help Workshop 4.1\">\r\n");
                sbIndex.Append("<!--Sitemap 1.0-->\r\n");
                sbIndex.Append("</HEAD><BODY>\r\n");
                sbIndex.Append("<OBJECT type=\"text /site properties\">\r\n");
                sbIndex.Append("\t<param name=\"ImageType\" value=\"Folder\">\r\n");
                sbIndex.Append("</OBJECT>\r\n");
                sbIndex.Append("<UL>\r\n");//索引文件不分层，所以不在内部添加<UL>标签。

                //索引文件内容
                //如果是子目录，在子目录“< UL >”Tag前加上：
                //<LI><OBJECT type="text/sitemap">
                //    <param name = "Name" value = "其它">
                //    </OBJECT >
                //有一层子目录就加一层“<UL>”Tag对

                CreateCHMIndexFile(new DirectoryInfo(Globals.PathOfWorkspace), "", sbIndex);

                //索引文件尾
                sbIndex.Append("</UL>\r\n");//索引文件不分层，所以不在内部添加<UL>标签。
                sbIndex.Append("</BODY></HTML>\r\n");

                #endregion

                File.WriteAllText(chmIndexFilePath, sbIndex.ToString(), gb2312);

                //第④步，调用hhc.exe编译
                //既然已经内置了编辑器，为什么不直接用它来手工修改工程文件再编译呢？

                if (hasInvalidatedFilePath)
                {
                    LMessageBox.Show("　　指定的工作区目录中某些文件的物理路径中含有特殊字符，会导致编译的 CHM 文件中的链接失效！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                }

                //编译总是会出错，所以干脆直接集成了Html Help Workshop到安装包里。
                LoadHHWInstalledPath();

                if (File.Exists(this.HHWInstalledPath))
                {
                    System.Diagnostics.Process.Start(this.HHWInstalledPath, chmProjectFilePath);
                }
                else
                {
                    LMessageBox.Show("　　未指定 Microsoft HTML Help Workshop.exe 的磁盘路径，无法调用！\r\n" +
                        "　　您可能需要到微软公司官方网站下载该工具并安装。",
                        Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                }

                //try
                //{
                //    //由于物理路径很可能包括空格，所以必须用双引号括起来，不然在命令行中会出错。
                //    //var cmd = "\"" + Globals.HtmlHelpCompilerFullPath + "\" \"" + chmProjectFilePath + "\"";

                //    System.Diagnostics.Process.Start(Globals.HtmlHelpCompilerFullPath, chmProjectFilePath);
                //}
                //catch (Exception ex)
                //{
                //    LMessageBox.Show(ex.Message);
                //}
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message + ex.StackTrace);
            }
        }

        private void LoadHHWInstalledPath()
        {
            if (File.Exists(this.HHWInstalledPath) == false)
            {
                var result = LMessageBox.Show("　　请指定 Microsoft Html Help Workshop 所在的磁盘路径：\r\n" +
                    "　　通常它的文件名应是 Microsoft HTML Help Workshop.exe 。\r\n\r\n　　要现在指定吗？", Globals.AppName,
                     MessageBoxButton.YesNo, MessageBoxImage.Information);

                if (result != MessageBoxResult.Yes) return;

                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Multiselect = false;
                ofd.Title = Globals.AppName + "请指定 Microsoft Html Help Workshop 路径：";
                ofd.Filter = "可执行文件(*.exe)|*.exe";

                if (ofd.ShowDialog() == true)
                {
                    this.ConfigManager.Set("HHWInstalledPath", ofd.FileName);
                    this.hhwInstalledPath = ofd.FileName;
                }
            }
        }

        /// <summary>
        /// 与InputBox中的同名方法不同：这里要考虑路径分割符和盘符。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool ValidateFilePath(string text)
        {
            if (string.IsNullOrEmpty(text))
                return false;

            if (text.Contains("/") ||
               //text.Contains("\\") ||     //路径中必须带分隔符，故这行不能加。
               //text.Contains(":") ||      //完整的路径中带盘符，故冒号不能加。
               text.Contains("*") ||
               text.Contains("\"") ||
               text.Contains("<") ||
               text.Contains(">") ||
               text.Contains("|"))
            {
                return false;
            }

            //带冒号的路径要考虑盘符的问题。
            if (text.Contains(":") || text.Contains("\\"))
            {
                if (text.Length <= 3)
                    return false;

                var subText1 = text.Substring(0, 3);
                if (subText1.EndsWith(":\\") == false)
                    return false;
                var firstChar = subText1[0];
                if (((firstChar >= 'a' && firstChar <= 'z') || (firstChar >= 'A' && firstChar <= 'Z')) == false)
                    return false;
                //以上可以确定不是盘符

                var subText2 = text.Substring(3);
                if (subText2.Contains(":"))
                    return false;//除盘符外，其它位置不应存在冒号

                if (subText2.StartsWith("\\") || subText2.EndsWith("\\"))
                    return false;
                //除开盘符后，路径的剩余部分不应以反斜杠开头；
                //即使不是以反斜杠开头，也仍然可能是个目录（目录以反斜杠结尾）
            }


            foreach (char c in text)
            {
                //全角字符就算是特殊符号也是可以的
                //全角小写字母
                if (c >= 'ａ' && c <= 'ｚ') continue;
                //全角大写字母
                if (c >= 'Ａ' && c <= 'Ｚ') continue;
                //！＂＃＄％＆＇（）＊＋，－．／
                if (c >= '！' && c <= '／') continue;
                //全角数字
                if (c >= '０' && c <= '９') continue;
                //：；＜＝＞？＠
                if (c >= '：' && c <= '＠') continue;
                //［＼］＾＿｀
                if (c >= '［' && c <= '｀') continue;
                //｛｜｝～
                if (c >= '｛' && c <= '～') continue;
                //￠￡￢￣￤￥
                if (c >= '￠' && c <= '￥') continue;
                //全角字符就算是特殊符号也是可以的

                //全角问号？就是>0x9fbb的
                if (c > 0x9fbb)
                {
                    if (c == '§') continue;
                    if (c == '☆') continue;
                    if (c == '★') continue;
                    if (c == '○') continue;
                    if (c == '●') continue;
                    if (c == '◎') continue;
                    if (c == '◇') continue;
                    if (c == '◆') continue;
                    if (c == '□') continue;
                    if (c == '■') continue;
                    if (c == '△') continue;
                    if (c == '▲') continue;
                    if (c == '※') continue;
                    if (c == '〓') continue;
                    if (c == '＃') continue;
                    if (c == '＆') continue;
                    if (c == '＠') continue;
                    if (c == '＿') continue;
                    if (c == '￣') continue;

                    return false;
                }

                if (c < 0x4e00)
                {
                    if (c == '\\') continue;
                    if (c == ':') continue;
                    if (c >= '0' && c <= '9') continue;
                    if (c == '_') continue;
                    if (c == '+') continue;
                    if (c == '-') continue;
                    if (c == '.') continue;//后缀名需要
                    if (c == '=') continue;
                    if (c == ' ') continue;
                    if (c == '(') continue;
                    if (c == ')') continue;
                    if (c == '）') continue;
                    if (c == '（') continue;
                    if (c >= '⑴' && c <= '⒇') continue;
                    if (c >= '①' && c <= '⑩') continue;
                    if (c >= '⒈' && c <= '⒛') continue;
                    if (c >= 'A' && c <= 'Z') continue;
                    if (c >= 'a' && c <= 'z') continue;

                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 生成CHM工程所需要的目录文件。
        /// </summary>
        /// <param name="directoryInfo"></param>
        /// <param name="prefixTab">第一层应传入空字符串，不需要\t。</param>
        /// <param name="prefixPath"></param>
        /// <param name="sb"></param>
        private bool CreateCHMContentFile(DirectoryInfo directoryInfo, string prefixTab, string prefixPath, StringBuilder sb)
        {
            if (directoryInfo == null || directoryInfo.Exists == false || prefixPath == null || sb == null) return false;

            if (prefixPath != "" && prefixPath.EndsWith("/") == false) prefixPath += "/";

            sb.Append($"{prefixTab}<UL>\r\n");

            bool hasInvalidatedFilePath = false;

            List<ChmContentEntry> chmContentEntries = new List<ChmContentEntry>();

            var files = directoryInfo.GetFiles();
            if (files.Length > 0)
            {
                foreach (var fileInfo in files)
                {
                    if (fileInfo.FullName.ToLower().EndsWith(".html") == false) continue;
                    if (fileInfo.FullName.ToLower().EndsWith("~.html")) continue;

                    if (fileInfo.Name.ToLower() == ("_" + directoryInfo + ".html").ToLower()) continue;//这个文件是指向CHM目录节点的文件。不在这里添加目录引用。

                    if (ValidateFilePath(fileInfo.FullName) == false)
                    {
                        hasInvalidatedFilePath = true;
                    }

                    string contentEntryName = GetTitleOfMdFile(fileInfo.FullName);

                    if (string.IsNullOrEmpty(contentEntryName))
                    {
                        var shortName = fileInfo.Name;
                        if (shortName.StartsWith("_") && shortName.Length > 1)
                        {
                            shortName = shortName.Substring(1);
                        }

                        if (shortName.ToLower() == "index.html" || shortName.ToLower() == "_index.html")
                        {
                            shortName = "目录";
                        }
                        else
                        {
                            if (shortName.ToLower().EndsWith(".html") && shortName.Length > 5)
                            {
                                shortName = shortName.Substring(0, shortName.Length - 5);
                            }
                        }

                        contentEntryName = shortName;
                    }

                    chmContentEntries.Add(new ChmContentEntry()
                    {
                        FileInfo = fileInfo,
                        ShortName = fileInfo.Name,
                        Title = contentEntryName,
                        IsFile = true,
                    });
                    //sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    //sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{contentEntryName}\">\r\n");
                    //sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{fileInfo.Name}\">\r\n");
                    //sb.Append($"{prefixTab}\t</OBJECT>\r\n");
                }
            }

            var subDirectories = directoryInfo.GetDirectories();
            if (subDirectories.Length > 0)
            {
                //prefixTab += "\t";
                foreach (var subDirectory in subDirectories)
                {
                    if (subDirectory.FullName.EndsWith("~")) continue;

                    var subFiles = subDirectory.GetFiles();
                    var htmlCount = 0;
                    string folderHtmlFile = null;
                    foreach (var subfile in subFiles)
                    {
                        if (subfile.FullName.ToLower().EndsWith(".html"))
                        {
                            htmlCount++;
                            if (subfile.Name.ToLower() == ("_" + subDirectory.Name + ".html").ToLower())
                            {
                                folderHtmlFile = subfile.Name;
                            }
                        }
                    }

                    chmContentEntries.Add(new ChmContentEntry()
                    {
                        IsDirectory = true,
                        DirectoryInfo = subDirectory,
                        ShortName = subDirectory.Name,
                        Title = subDirectory.Name,
                        FolderHtmlFile = folderHtmlFile,
                    });

                    ////先加目录，即使是空目录也应该添加一条目录条目
                    //sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    //sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{subDirectory.Name}\">\r\n");

                    ////目录节点在CHM中指向的文件应是该目录下名为“_目录短名.html”的文件。
                    //if (string.IsNullOrWhiteSpace(folderHtmlFile) == false)
                    //{
                    //    sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{subDirectory.Name}\\{folderHtmlFile}\">\r\n");
                    //}

                    //sb.Append($"{prefixTab}\t</OBJECT>\r\n");

                    //var subPrefixPath = prefixPath + subDirectory.Name;

                    ////因为是递归的，所以不能直接用下面这行：
                    ////hasInvalidatedFilePath = CreateContentDirectory(subDirectiry, prefixTab, subPrefixPath, sb);

                    //if (CreateCHMContentFile(subDirectory, prefixTab, subPrefixPath, sb) == true)
                    //{
                    //    hasInvalidatedFilePath = true;
                    //}
                }
                //prefixTab = prefixTab.Substring(0, prefixTab.Length - 1);
            }

            List<ChmContentEntry> tempList2 = new List<ChmContentEntry>();
            List<ChmContentEntry> tempList3 = new List<ChmContentEntry>();

            foreach (var c in chmContentEntries)
            {
                if (c.ShortName.StartsWith("_"))
                {
                    tempList2.Add(c);
                }
                else
                {
                    tempList3.Add(c);
                }
            }

            tempList2.Sort(new ChmContentEntryCompare());
            tempList3.Sort(new ChmContentEntryCompare());

            foreach (var ce in tempList2)
            {
                if (ce.IsFile)
                {
                    sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{ce.Title}\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{ce.FileInfo.Name}\">\r\n");
                    sb.Append($"{prefixTab}\t</OBJECT>\r\n");
                }
                else if (ce.IsDirectory)
                {
                    prefixTab += "\t";
                    //先加目录，即使是空目录也应该添加一条目录条目
                    sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{ce.DirectoryInfo.Name}\">\r\n");

                    //目录节点在CHM中指向的文件应是该目录下名为“_目录短名.html”的文件。
                    if (string.IsNullOrWhiteSpace(ce.FolderHtmlFile) == false)
                    {
                        sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{ce.DirectoryInfo.Name}\\{ce.FolderHtmlFile}\">\r\n");
                    }

                    sb.Append($"{prefixTab}\t</OBJECT>\r\n");

                    var subPrefixPath = prefixPath + ce.DirectoryInfo.Name;

                    //因为是递归的，所以不能直接用下面这行：
                    //hasInvalidatedFilePath = CreateContentDirectory(subDirectiry, prefixTab, subPrefixPath, sb);

                    if (CreateCHMContentFile(ce.DirectoryInfo, prefixTab, subPrefixPath, sb) == true)
                    {
                        hasInvalidatedFilePath = true;
                    }
                    prefixTab = prefixTab.Substring(0, prefixTab.Length - 1);
                }
            }

            foreach (var ce in tempList3)
            {
                if (ce.IsFile)
                {
                    sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{ce.Title}\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{ce.FileInfo.Name}\">\r\n");
                    sb.Append($"{prefixTab}\t</OBJECT>\r\n");
                }
                else if (ce.IsDirectory)
                {
                    prefixTab += "\t";
                    //先加目录，即使是空目录也应该添加一条目录条目
                    sb.Append($"{prefixTab}<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    sb.Append($"{prefixTab}\t<param name =\"Name\" value=\"{ce.DirectoryInfo.Name}\">\r\n");

                    //目录节点在CHM中指向的文件应是该目录下名为“_目录短名.html”的文件。
                    if (string.IsNullOrWhiteSpace(ce.FolderHtmlFile) == false)
                    {
                        sb.Append($"{prefixTab}\t<param name =\"Local\" value=\"{prefixPath}{ce.DirectoryInfo.Name}\\{ce.FolderHtmlFile}\">\r\n");
                    }

                    sb.Append($"{prefixTab}\t</OBJECT>\r\n");

                    var subPrefixPath = prefixPath + ce.DirectoryInfo.Name;

                    //因为是递归的，所以不能直接用下面这行：
                    //hasInvalidatedFilePath = CreateContentDirectory(subDirectiry, prefixTab, subPrefixPath, sb);

                    if (CreateCHMContentFile(ce.DirectoryInfo, prefixTab, subPrefixPath, sb) == true)
                    {
                        hasInvalidatedFilePath = true;
                    }
                    prefixTab = prefixTab.Substring(0, prefixTab.Length - 1);
                }
            }

            sb.Append($"{prefixTab}</UL>\r\n");
            return hasInvalidatedFilePath;
        }


        /// <summary>
        /// 生成CHM工程所需要的索引文件。与目录文件不同，索引文件不分层级。
        /// </summary>
        /// <param name="directoryInfo"></param>
        /// <param name="prefixPath"></param>
        /// <param name="sb"></param>
        private bool CreateCHMIndexFile(DirectoryInfo directoryInfo, string prefixPath, StringBuilder sb)
        {
            if (directoryInfo == null || directoryInfo.Exists == false || prefixPath == null || sb == null) return false;

            if (prefixPath != "" && prefixPath.EndsWith("/") == false) prefixPath += "/";

            bool hasInvalidatedFilePath = false;

            var files = directoryInfo.GetFiles();
            if (files.Length > 0)
            {
                foreach (var fileInfo in files)
                {
                    if (fileInfo.FullName.ToLower().EndsWith(".html") == false) continue;
                    if (fileInfo.FullName.ToLower().EndsWith("~.html")) continue;

                    if (ValidateFilePath(fileInfo.FullName) == false)
                    {
                        hasInvalidatedFilePath = true;
                    }

                    string contentEntryName = GetTitleOfMdFile(fileInfo.FullName);

                    if (string.IsNullOrEmpty(contentEntryName))
                    {
                        var shortName = fileInfo.Name;
                        if (shortName.StartsWith("_") && shortName.Length > 1)
                        {
                            shortName = shortName.Substring(1);
                        }

                        if (shortName.ToLower() == "index.html" || shortName.ToLower() == "_index.html")
                        {
                            shortName = "目录";
                        }
                        else
                        {
                            if (shortName.ToLower().EndsWith(".html") && shortName.Length > 5)
                            {
                                shortName = shortName.Substring(0, shortName.Length - 5);
                            }
                        }

                        contentEntryName = shortName;
                    }

                    sb.Append("\t<LI> <OBJECT type = \"text/sitemap\">\r\n");
                    sb.Append($"\t\t<param name =\"Name\" value=\"{contentEntryName}\">\r\n");
                    sb.Append($"\t\t<param name =\"Local\" value=\"{prefixPath}{fileInfo.Name}\">\r\n");
                    sb.Append("\t\t</OBJECT>\r\n");
                }
            }

            var subDirectories = directoryInfo.GetDirectories();
            if (subDirectories.Length > 0)
            {
                foreach (var subDirectiry in subDirectories)
                {
                    if (subDirectiry.FullName.EndsWith("~")) continue;

                    var subFiles = subDirectiry.GetFiles();
                    var htmlCount = 0;
                    foreach (var subfile in subFiles)
                    {
                        if (subfile.FullName.ToLower().EndsWith(".html")) htmlCount++;
                    }

                    if (htmlCount > 0)
                    {
                        //索引文件不加目录
                        //sb.Append($"\t<LI> <OBJECT type = \"text/sitemap\">\r\n");
                        //sb.Append($"\t\t<param name =\"Name\" value=\"{subDirectiry.Name}\">\r\n");
                        //sb.Append($"\t\t</OBJECT>\r\n");

                        var subPrefixPath = subDirectiry.Name;

                        //因为是递归的，所以不能直接用下面这行：
                        //hasInvalidatedFilePath = CreateContentDirectory(subDirectiry, prefixTab, subPrefixPath, sb);

                        if (CreateCHMIndexFile(subDirectiry, subPrefixPath, sb) == true)
                        {
                            hasInvalidatedFilePath = true;
                        }
                    }
                }
            }

            return hasInvalidatedFilePath;
        }

        private string GetTitleOfMdFile(string mdFileFullName)
        {
            if (mdFileFullName != null && mdFileFullName.ToLower().EndsWith(".html"))
            {
                mdFileFullName = mdFileFullName.Substring(0, mdFileFullName.Length - 5) + ".md";
            }

            if (File.Exists(mdFileFullName) == false) return "";

            var lines = File.ReadLines(mdFileFullName);
            foreach (string s in lines)
            {
                if (s.StartsWith("%")) return s.Substring(1).Trim();
                var title = CustomMarkdownSupport.GetDocumentTitle(s);
                if (string.IsNullOrWhiteSpace(title) == false) return title;

                int startIndex = s.IndexOf("标题＞＞");
                int endIndex = s.IndexOf("＜＜标题");
                if (startIndex >= 0 && endIndex >= 0)
                {
                    return s.Substring(startIndex + 4, endIndex - startIndex - 4);
                }
            }

            return "";
        }

        private void miCompileAllMdFilesOfWorkspace_Click(object sender, RoutedEventArgs e)
        {
            if (miUtf8.IsChecked)
            {
                var result = LMessageBox.Show("　　当前选择的字符编码是UTF-8，这种编码的Html不适合编译成CHM文档。要自动改成GB2312字符编码吗？",
                                    Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);

                if (result == MessageBoxResult.Cancel) return;
                if (result == MessageBoxResult.OK)
                {
                    miGb2312.IsChecked = true;
                    miGb2312_Click(sender, e);
                }
            }

            CompileWholeWorkspace();

            LMessageBox.Show("　　已将工作区内所有Markdown文件编译为Html文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void CompileWholeWorkspace()
        {
            List<CompiledFileInfo> compiledFilePaths = new List<CompiledFileInfo>();

            CompileAllMdFiles(ref compiledFilePaths);

            //生成一个html索引文档。
            BuildIndexOfCompiledHtmlFiles(compiledFilePaths);
        }

        private void miOpenCHMProject_Click(object sender, RoutedEventArgs e)
        {
            LoadHHWInstalledPath();

            if (File.Exists(this.HHWInstalledPath) == false)
            {
                LMessageBox.Show("　　未指定 Microsoft HTML Help Workshop.exe 的磁盘路径，无法调用！\r\n" +
                    "　　您可能需要到微软公司官方网站下载该工具并安装。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            var prefixOfFiles = Globals.PathOfWorkspace + Globals.WorkspaceShortName;
            var chmProjectFilePath = prefixOfFiles + ".hhp";

            if (File.Exists(chmProjectFilePath))
            {
                System.Diagnostics.Process.Start(this.HHWInstalledPath, chmProjectFilePath);
            }
            else
            {
                LMessageBox.Show("　　尚未创建工程。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miClearCompiledHtmlFiles_Click(object sender, RoutedEventArgs e)
        {
            var result = LMessageBox.Show("　　此功能将删除工作区目录下所有Html文件，无论它是不是由本程序编译而成！\r\n\r\n　　真的要继续吗？",
                Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Warning);

            if (result != MessageBoxResult.Yes) return;

            DeleteHtmlFiles(Globals.PathOfWorkspace);
        }

        private void DeleteHtmlFiles(string directoryPath)
        {
            if (Directory.Exists(directoryPath) == false) return;
            var directoryShortName = Path.GetDirectoryName(directoryPath);
            if (directoryShortName.EndsWith("~")) return;

            var directoryInfo = new DirectoryInfo(directoryPath);
            var files = directoryInfo.GetFiles();
            foreach (var file in files)
            {
                if (file.Extension.ToLower() != ".html") continue;
                if (Path.GetFileNameWithoutExtension(file.FullName).EndsWith("~")) continue;

                File.Delete(file.FullName);
            }

            var subDirectiries = directoryInfo.GetDirectories();
            foreach (var subDirectory in subDirectiries)
            {
                DeleteHtmlFiles(subDirectory.FullName);
            }
        }

        private void miFillblankMode_Click(object sender, RoutedEventArgs e)
        {
            this.compileCodeToFillBlank =
            miFillblankMode.IsChecked = !miFillblankMode.IsChecked;
            this.ConfigManager.Set("CompileCodeToFillBlank", miFillblankMode.IsChecked.ToString());
        }

        private void miDeleteHistoryWorkspaceEntry_Click(object sender, RoutedEventArgs e)
        {
            if (lbxHistoryWorkspaces.SelectedIndex < 0)
            {
                LMessageBox.Show("　　请先选择要删除的条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            lbxHistoryWorkspaces.Items.RemoveAt(lbxHistoryWorkspaces.SelectedIndex);

            StringBuilder sb = new StringBuilder();
            foreach (RecentDirectoryListBoxItem item in lbxHistoryWorkspaces.Items)
            {
                sb.Insert(0, "\r\n" + item.DirectoryPath);
            }

            File.WriteAllText(Globals.PathOfHistoryWorkspaceFileFullName, sb.ToString(), new UnicodeEncoding());
        }

        private void miDeleteHistoryOutputEntry_Click(object sender, RoutedEventArgs e)
        {
            if (lbxHistoryOutport.SelectedIndex < 0)
            {
                LMessageBox.Show("　　请先选择要删除的条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            lbxHistoryOutport.Items.RemoveAt(lbxHistoryOutport.SelectedIndex);

            StringBuilder sb = new StringBuilder();
            foreach (RecentDirectoryListBoxItem item in lbxHistoryOutport.Items)
            {
                sb.Insert(0, "\r\n" + item.DirectoryPath);
            }

            File.WriteAllText(Globals.PathOfHistoryOutputFileFullName, sb.ToString(), new UnicodeEncoding());
        }

        private void miDeleteHistoryWorkspaceFile_Click(object sender, RoutedEventArgs e)
        {
            if (lbxHistoryWorkspaces.Items.Count <= 0) return;

            var result = LMessageBox.Show("　　真的要清除“历史工作区列表”吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result != MessageBoxResult.Yes) return;

            try
            {
                if (File.Exists(Globals.PathOfHistoryWorkspaceFileFullName))
                {
                    File.Delete(Globals.PathOfHistoryWorkspaceFileFullName);
                }

                lbxHistoryWorkspaces.Items.Clear();
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miDeleteHistoryOutputFile_Click(object sender, RoutedEventArgs e)
        {
            if (lbxHistoryOutport.Items.Count <= 0) return;

            var result = LMessageBox.Show("　　真的要清除“历史导出目录列表”吗？", Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result != MessageBoxResult.Yes) return;

            try
            {
                if (File.Exists(Globals.PathOfHistoryOutputFileFullName))
                {
                    File.Delete(Globals.PathOfHistoryOutputFileFullName);
                }

                lbxHistoryOutport.Items.Clear();
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miInsertAnchor_Click(object sender, RoutedEventArgs e)
        {
            MarkdownEditor eti = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (eti == null) return;

            eti.EditorBase.SelectedText = "\r\n\r\n[](@请输入锚名称)\r\n\r\n";
            eti.EditorBase.Select(eti.EditorBase.SelectionStart + 8, 6);
        }

        private void miMaxSizeWindow_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Maximized;
        }

        private void miTextWrap_Click(object sender, RoutedEventArgs e)
        {
            this.TextAutoWrap =
            this.miTextAutoWrap.IsChecked = !this.miTextAutoWrap.IsChecked;
            this.configManager.Set("TextAutoWrap", miTextAutoWrap.IsChecked.ToString());
        }

        private void miOpenFileOrFolder_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中工作区目录中某一个条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi.IsMarkdownFilePath)
            {
                OpenDocuments(new string[] { wtvi.FullPath });
            }
            else if (wtvi.IsDirectoryExists || wtvi.IsImageFileExist)
            {
                System.Diagnostics.Process.Start("explorer.exe", wtvi.FullPath);
            }
            else
            {
                LMessageBox.Show("　　只有Markdown文档或目录或图像文件才可以打开！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void miOpenDocument_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中工作区目录中某一个条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //如果是MD文件，直接打开；
            //如果是Directory，打开它的默认MD文件；
            //如果是Image，调用Explorer使用系统默认图片应用程序打开。

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi.IsMarkdownFilePath)
            {
                OpenDocuments(new string[] { wtvi.FullPath });
            }
            else if (wtvi.IsDirectoryExists)
            {
                try
                {
                    DirectoryInfo di = new DirectoryInfo(wtvi.FullPath);
                    if (di.Name.EndsWith("~"))
                    {
                        LMessageBox.Show("　　以波型符结尾的是资源文件夹，不允许创建对应 Markdown 文件。", Globals.AppName,
                          MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    var directoryMdFileFullName = (di.FullName.EndsWith("\\") ? di.FullName : (di.FullName + "\\")) + "_" + di.Name + ".md";
                    if (File.Exists(directoryMdFileFullName) == false)
                    {
                        using (StreamWriter sw = File.CreateText(directoryMdFileFullName))
                        {
                            sw.Write($"%{di.Name}\r\n{DateTime.Now.ToString()}");
                        }
                    }

                    OpenDocuments(new string[] { directoryMdFileFullName });
                }
                catch (Exception ex)
                {
                    LMessageBox.Show(ex.Message);
                }
            }
            else if (wtvi.IsImageFileExist)
            {
                System.Diagnostics.Process.Start("explorer.exe", wtvi.FullPath);
            }
        }

        private void miOpenFolder_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选中工作区目录中某一个条目！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //如果是MD文件或图片，调用Explorer打开它所在的目录;
            //如果是目录本身，调用Explorer，直接打开该目录。
            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            if (wtvi.IsMarkdownFilePath)
            {
                FileInfo fi = new FileInfo(wtvi.FullPath);
                System.Diagnostics.Process.Start("explorer.exe", fi.Directory.FullName);
            }
            else if (wtvi.IsDirectoryExists || wtvi.IsImageFileExist)
            {
                System.Diagnostics.Process.Start("explorer.exe", wtvi.FullPath);
            }
        }

        private void miNewBrotherDirectory_Click(object sender, RoutedEventArgs e)
        {
            if (tvWorkDirectory.SelectedItem == null)
            {
                LMessageBox.Show("　　请在工作区中选择一个目录或Markdown文件再执行此操作。", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            WorkspaceTreeViewItem wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;
            DirectoryInfo di = new DirectoryInfo(wtvi.FullPath);
            if (di.Name.EndsWith("~"))
            {
                LMessageBox.Show("　　以波形符结尾的目录是程序自动管理的资源目录，不允许在其下新建子目录。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            string parentDirectory = null;
            if (Directory.Exists(wtvi.FullPath))
            {
                parentDirectory = new DirectoryInfo(wtvi.FullPath).Parent.FullName;
            }
            else if (File.Exists(wtvi.FullPath))
            {
                if (wtvi.IsMarkdownFilePath)
                {
                    parentDirectory = new FileInfo(wtvi.FullPath).Directory.FullName;
                }
            }

            if (parentDirectory == null)
            {
                LMessageBox.Show("　　请在工作区中选择一个目录或Markdown文件再执行此操作。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var parentItem = wtvi.ParentWorkspaceTreeViewItem;

            if (wtvi.FullPath.ToLower() == Globals.PathOfWorkspace.ToLower())
            {
                LMessageBox.Show("　　不允许建立与工作区同级的子目录，新目录将会被包含在工作区目录中。",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                parentDirectory = Globals.PathOfWorkspace;
                parentItem = wtvi;//工作区目录比较特殊
            }

            var newShortDirectoryName = InputBox.Show(Globals.AppName, "请输入新目录名：", "", true);
            if (string.IsNullOrEmpty(newShortDirectoryName))
            {
                LMessageBox.Show("　　目录名称不能为空！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            try
            {
                var newFullPath = (parentDirectory.EndsWith("\\") ? parentDirectory : (parentDirectory + "\\")) + newShortDirectoryName;
                if (Directory.Exists(newFullPath) == false)
                {
                    Directory.CreateDirectory(newFullPath);
                }
                var newItem = new WorkspaceTreeViewItem(newFullPath, Globals.MainWindow);
                if (parentItem != null)
                {
                    parentItem.Items.Add(newItem);
                }
                newItem.IsSelected = true;
            }
            catch (Exception ex)
            {
                LMessageBox.Show("　　创建目录失败！错误消息：\r\n" + ex.Message,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Error);

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="searchHeader">[-]表示未开始；[%]表示正在进行；[+]表示已完成；[#]表示已废弃。</param>
        public void FindTodoList(string searchHeader)
        {
            tvTaskList.Items.Clear();
            FindTodoList((cmbSearchArea.SelectedItem as ComboBoxItem).Tag.ToString(), searchHeader);

            if (tvTaskList.Items.Count <= 0)
            {
                tvTaskList.Items.Add(new TreeViewItem() { Header = "<没找到任务列表...>", });
            }

            cmbFindText.Items.Add(cmbFindText.Text);

            if (rightToolBar.SelectedIndex != 3)
            {
                rightToolBar.SelectedIndex = 3;
            }

            if (cdRightToolsArea.ActualWidth < 140)
            {
                cdLeftToolsArea.Width = new GridLength(140);
            }
        }

        /// <summary>
        /// 在磁盘文档或当前正在编辑的文档之间查找指定文本内容。
        /// </summary>
        /// <param name="searchArea">搜索的文档的范围（当前文档、打开的文档、全工作区）。</param>
        /// <param name="searchHeader">查找的任务项目的类型（[-]，未开始；[%]正在进行；[+]已完成；[#]已废弃。</param>
        private void FindTodoList(string searchArea, string searchHeader)
        {
            tvTaskList.Items.Clear();
            switch (searchArea)
            {
                case "ActiveDocument":
                    {
                        var efi = this.mainTabControl.SelectedItem as MarkdownEditor;
                        if (efi != null)
                        {
                            FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(efi.FullFilePath, efi.ShortFileName);
                            var lines = efi.EditorBase.Document.Lines;
                            for (int i = 0; i < lines.Count; i++)
                            {
                                var line = lines[i];
                                var lineText = efi.EditorBase.Document.GetText(line.Offset, line.Length);
                                if (string.IsNullOrEmpty(lineText)) continue;

                                var tmp = lineText.Replace(" ", "").Replace("\t", "").Replace("　", "").Replace("：", ":");
                                bool isTodoListItem = false;
                                Brush foreground = null;
                                if (tmp.StartsWith("[-]"))
                                {
                                    isTodoListItem = true;
                                    foreground = Brushes.Red;
                                }
                                else if (tmp.StartsWith("[%]"))
                                {
                                    isTodoListItem = true;
                                    foreground = Brushes.Green;
                                }
                                else if (tmp.StartsWith("[+]"))
                                {
                                    isTodoListItem = true;
                                    foreground = Brushes.DarkOrange;
                                }
                                else if (tmp.StartsWith("[#]"))
                                {
                                    isTodoListItem = true;
                                    foreground = Brushes.Brown;
                                }

                                if (isTodoListItem)
                                {
                                    bool isSearchItem = false;
                                    if (string.IsNullOrEmpty(searchHeader))
                                    {
                                        isSearchItem = true;
                                    }
                                    else if (tmp.StartsWith(searchHeader))
                                    {
                                        isSearchItem = true;
                                    }
                                    else
                                    {
                                        if (searchHeader == "![+]")
                                        {
                                            if (tmp.StartsWith("[-]") || tmp.StartsWith("[%]") || tmp.StartsWith("[#]"))
                                            {
                                                isSearchItem = true;
                                            }
                                        }
                                    }

                                    if (isSearchItem)
                                    {
                                        int preIndex, nextIndex;
                                        preIndex = lineText.IndexOf('[') + 1;
                                        nextIndex = lineText.IndexOf(']') - 1;
                                        TextDecorationCollection textDecoration = null;
                                        if (tmp.StartsWith("[#]") || tmp.StartsWith("[+]"))
                                        {
                                            textDecoration = TextDecorations.Strikethrough;
                                        }

                                        FindLineTreeViewItem newItem;
                                        if (preIndex >= 0 && nextIndex >= 0 && nextIndex >= preIndex)
                                        {
                                            newItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, preIndex, nextIndex, lineText, foreground,
                                                 FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                            fdi.Items.Add(newItem);
                                        }
                                        else
                                        {
                                            newItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, 0, nextIndex, lineText, foreground,
                                                 FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                            fdi.Items.Add(newItem);
                                        }

                                        for (int j = i + 1; j < lines.Count; j++)
                                        {
                                            var line2 = lines[j];
                                            var line2Text = efi.EditorBase.Document.GetText(line2.Offset, line2.Length);
                                            if (CustomMarkdownSupport.IsTodoLine(line2Text)) break;//到下一个任务列表项就停止。

                                            if (CustomMarkdownSupport.IsDateLine(line2Text))
                                            {
                                                var leftIndex = line2Text.IndexOf("[");
                                                var rightIndex = line2Text.IndexOf("]");
                                                if (leftIndex >= 0 && rightIndex > leftIndex)
                                                {
                                                    var newDateItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName,
                                                        line2.LineNumber, leftIndex + 1, rightIndex - leftIndex - 1, line2Text, foreground, FindLineTreeViewItem.ItemType.Normal);
                                                    newItem.Items.Add(newDateItem);
                                                }
                                                else {
                                                    var newDateItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName,
                                                        line2.LineNumber, 0, 0, line2Text, foreground, FindLineTreeViewItem.ItemType.Normal);
                                                    newItem.Items.Add(newDateItem);
                                                }
                                            }
                                        }

                                        newItem.IsExpanded = true;
                                    }
                                }
                            }

                            if (fdi.Items.Count > 0)
                            {
                                this.tvTaskList.Items.Add(fdi);
                                fdi.IsExpanded = true;
                                (fdi.Items[0] as TreeViewItem).IsSelected = true;
                            }
                        }
                        break;
                    }
                case "OpenedDocuments":
                    {
                        foreach (var item in this.mainTabControl.Items)
                        {
                            var efi = item as MarkdownEditor;
                            if (efi != null)
                            {
                                FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(efi.FullFilePath, efi.ShortFileName);
                                var lines = efi.EditorBase.Document.Lines;
                                for (int i = 0; i < lines.Count; i++)
                                {
                                    var line = lines[i];
                                    var lineText = efi.EditorBase.Document.GetText(line.Offset, line.Length);
                                    if (string.IsNullOrEmpty(lineText)) continue;

                                    var tmp = lineText.Replace(" ", "").Replace("\t", "").Replace("　", "").Replace("：", ":");
                                    bool isTodoListItem = false;
                                    Brush foreColor = null;
                                    if (tmp.StartsWith("[-]"))
                                    {
                                        isTodoListItem = true;
                                        foreColor = Brushes.Red;
                                    }
                                    else if (tmp.StartsWith("[%]"))
                                    {
                                        isTodoListItem = true;
                                        foreColor = Brushes.Green;
                                    }
                                    else if (tmp.StartsWith("[+]"))
                                    {
                                        isTodoListItem = true;
                                        foreColor = Brushes.DarkOrange;
                                    }
                                    else if (tmp.StartsWith("[#]"))
                                    {
                                        isTodoListItem = true;
                                        foreColor = Brushes.Brown;
                                    }

                                    if (isTodoListItem)
                                    {
                                        bool isSearchItem = false;
                                        if (string.IsNullOrEmpty(searchHeader))
                                        {
                                            isSearchItem = true;
                                        }
                                        else if (tmp.StartsWith(searchHeader))
                                        {
                                            isSearchItem = true;
                                        }
                                        else
                                        {
                                            if (searchHeader == "![+]")
                                            {
                                                if (tmp.StartsWith("[-]") || tmp.StartsWith("[%]") || tmp.StartsWith("[#]"))
                                                {
                                                    isSearchItem = true;
                                                }
                                            }
                                        }

                                        if (isSearchItem)
                                        {
                                            int preIndex, nextIndex;
                                            preIndex = lineText.IndexOf('[') + 1;
                                            nextIndex = lineText.IndexOf(']') - 1;
                                            TextDecorationCollection textDecoration = null;
                                            if (tmp.StartsWith("[#]") || tmp.StartsWith("[+]"))
                                            {
                                                textDecoration = TextDecorations.Strikethrough;
                                            }

                                            FindLineTreeViewItem newItem;
                                            if (preIndex >= 0 && nextIndex >= 0 && nextIndex >= preIndex)
                                            {
                                                newItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, preIndex, nextIndex, lineText, foreColor,
                                                     FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                                fdi.Items.Add(newItem);
                                            }
                                            else
                                            {
                                                newItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName, line.LineNumber, 0, nextIndex, lineText, foreColor,
                                                     FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                                fdi.Items.Add(newItem);
                                            }

                                            for (int j = i + 1; j < lines.Count; j++)
                                            {
                                                var line2 = lines[j];
                                                var line2Text = efi.EditorBase.Document.GetText(line2.Offset, line2.Length);
                                                if (CustomMarkdownSupport.IsTodoLine(line2Text)) break;//到下一个任务列表项就停止。

                                                if (CustomMarkdownSupport.IsDateLine(line2Text))
                                                {
                                                    var leftIndex = line2Text.IndexOf("[");
                                                    var rightIndex = line2Text.IndexOf("]");
                                                    if (leftIndex >= 0 && rightIndex > leftIndex)
                                                    {
                                                        var newDateItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName,
                                                            line2.LineNumber, leftIndex + 1, rightIndex - leftIndex - 1, line2Text, foreColor, FindLineTreeViewItem.ItemType.Normal);
                                                        newItem.Items.Add(newDateItem);
                                                    }
                                                    else {
                                                        var newDateItem = new FindLineTreeViewItem(efi.FullFilePath, efi.ShortFileName,
                                                            line2.LineNumber, 0, 0, line2Text, foreColor, FindLineTreeViewItem.ItemType.Normal);
                                                        newItem.Items.Add(newDateItem);
                                                    }
                                                }
                                            }

                                            newItem.IsExpanded = true;
                                        }
                                    }
                                }

                                if (fdi.Items.Count > 0)
                                {
                                    this.tvTaskList.Items.Add(fdi);
                                    fdi.IsExpanded = true;
                                    (fdi.Items[0] as TreeViewItem).IsSelected = true;
                                }
                            }
                        }
                        break;
                    }
                case "AllFiles":
                    {
                        //这个需要用到递归
                        FindTodoListInAllFiles(Globals.PathOfWorkspace, searchHeader);
                        break;
                    }
            }
        }

        private MarkdownEditor GetOpenedEditor(string mdFileFullName)
        {
            if (string.IsNullOrWhiteSpace(mdFileFullName)) return null;

            if (this.mainTabControl.Items.Count > 0)
            {
                foreach (var ue in this.mainTabControl.Items)
                {
                    var editor = ue as MarkdownEditor;
                    if (editor == null) continue;
                    if (editor.FullFilePath == null) continue;

                    if (editor.FullFilePath.ToLower() == mdFileFullName.ToLower()) return editor;
                }
            }

            return null;
        }

        /// <summary>
        /// 这是个递归方法。
        /// </summary>
        /// <param name="directoryPath">要查找的目录</param>
        /// <param name="searchHeader">要查找的任务列表的类型（[-]为未开始；[%]为正在进行；[+]是已完成；[#]是已废弃。）</param>
        private void FindTodoListInAllFiles(string directoryPath, string searchHeader)
        {
            if (Directory.Exists(directoryPath) == false) return;
            var directory = new DirectoryInfo(directoryPath);

            //先处理当前目录下的文件
            var childFilesInfos = directory.GetFiles();

            foreach (var childFileInfo in childFilesInfos)
            {
                if (childFileInfo.FullName.ToLower().EndsWith(".md") == false) continue;

                string[] lines;

                //已打开的文件，按打开的情况算，没打开的，按磁盘文本查找。
                var fileEditor = GetOpenedEditor(childFileInfo.FullName);
                if (fileEditor != null)
                {
                    lines = fileEditor.EditorBase.Text.Replace("\r", "").Split(new char[] { '\n' });
                }
                else
                {
                    lines = File.ReadAllLines(childFileInfo.FullName);
                }

                FindDocumentTreeViewItem fdi = new FindDocumentTreeViewItem(childFileInfo.FullName, childFileInfo.Name);

                var lineNum = 0;
                for (int i = 0; i < lines.Length; i++)
                {
                    lineNum++;

                    var lineText = lines[i];

                    if (string.IsNullOrEmpty(lineText)) continue;

                    var tmp = lineText.Replace(" ", "").Replace("\t", "").Replace("　", "").Replace("：", ":");
                    bool isTodoListItem = false;
                    Brush foreColor = null;
                    if (tmp.StartsWith("[-]"))
                    {
                        isTodoListItem = true;
                        foreColor = Brushes.Red;
                    }
                    else if (tmp.StartsWith("[%]"))
                    {
                        isTodoListItem = true;
                        foreColor = Brushes.Green;
                    }
                    else if (tmp.StartsWith("[+]"))
                    {
                        isTodoListItem = true;
                        foreColor = Brushes.DarkOrange;
                    }
                    else if (tmp.StartsWith("[#]"))
                    {
                        isTodoListItem = true;
                        foreColor = Brushes.Brown;
                    }

                    if (isTodoListItem)
                    {
                        bool isSearchItem = false;
                        if (string.IsNullOrEmpty(searchHeader))
                        {
                            isSearchItem = true;
                        }
                        else if (tmp.StartsWith(searchHeader))
                        {
                            isSearchItem = true;
                        }
                        else
                        {
                            if (searchHeader == "![+]")
                            {
                                if (tmp.StartsWith("[-]") || tmp.StartsWith("[%]") || tmp.StartsWith("[#]"))
                                {
                                    isSearchItem = true;
                                }
                            }
                        }

                        if (isSearchItem)
                        {
                            int preIndex, nextIndex;
                            preIndex = lineText.IndexOf('[') + 1;
                            nextIndex = lineText.IndexOf(']') - 1;
                            TextDecorationCollection textDecoration = null;
                            if (tmp.StartsWith("[#]") || tmp.StartsWith("[+]"))
                            {
                                textDecoration = TextDecorations.Strikethrough;
                            }

                            FindLineTreeViewItem newItem;
                            if (preIndex >= 0 && nextIndex >= 0 && nextIndex >= preIndex)
                            {
                                newItem = new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name, lineNum, preIndex, nextIndex, lineText, foreColor,
                                     FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                fdi.Items.Add(newItem);
                            }
                            else
                            {
                                newItem = new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name, lineNum, 0, nextIndex, lineText, foreColor,
                                     FindLineTreeViewItem.ItemType.TodoListItem, textDecoration);
                                fdi.Items.Add(newItem);
                            }

                            for (int j = i + 1; j < lines.Length; j++)
                            {
                                var line2Text = lines[j];
                                if (CustomMarkdownSupport.IsTodoLine(line2Text)) break;//到下一个任务列表项就停止。

                                if (CustomMarkdownSupport.IsDateLine(line2Text))
                                {
                                    var leftIndex = line2Text.IndexOf("[");
                                    var rightIndex = line2Text.IndexOf("]");
                                    if (leftIndex >= 0 && rightIndex > leftIndex)
                                    {
                                        var newDateItem = new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name,
                                            j + 1, leftIndex + 1, rightIndex - leftIndex - 1, line2Text, foreColor, FindLineTreeViewItem.ItemType.Normal);
                                        newItem.Items.Add(newDateItem);
                                    }
                                    else {
                                        var newDateItem = new FindLineTreeViewItem(childFileInfo.FullName, childFileInfo.Name,
                                            j + 1, 0, 0, line2Text, foreColor, FindLineTreeViewItem.ItemType.Normal);
                                        newItem.Items.Add(newDateItem);
                                    }
                                }
                            }

                            newItem.IsExpanded = true;
                        }
                    }
                }

                if (fdi.Items.Count > 0)
                {
                    this.tvTaskList.Items.Add(fdi);
                    fdi.IsExpanded = true;
                }
            }

            //再递归处理子目录下的文件
            var childDirectories = directory.GetDirectories();
            foreach (var childDirectory in childDirectories)
            {
                FindTodoListInAllFiles(childDirectory.FullName, searchHeader);
            }
        }

        private void miSwitchTaskListItemOrDateTimeLineState_Click(object sender, RoutedEventArgs e)
        {
            if (this.mainTabControl.SelectedItem == null) return;

            var editor = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (editor == null) return;

            editor.EditorBase.SwitchTaskListItemOrDateTimeLineState(false, false);
        }

        private void miSetAsListItem_Click(object sender, RoutedEventArgs e)
        {
            if (this.mainTabControl.SelectedItem == null) return;

            var editor = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (editor == null) return;

            editor.EditorBase.SwitchListMark(true);
        }

        private void miDeleteListItemMark_Click(object sender, RoutedEventArgs e)
        {
            if (this.mainTabControl.SelectedItem == null) return;

            var editor = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (editor == null) return;

            editor.EditorBase.SwitchListMark(false);
        }

        private void miSetAsCurrentWorkspace_Click(object sender, RoutedEventArgs e)
        {
            if (lbxHistoryWorkspaces.SelectedItem == null)
            {
                LMessageBox.Show("　　请先选定一个历史工作区记录！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var item = lbxHistoryWorkspaces.SelectedItem as RecentDirectoryListBoxItem;
            if (item == null) return;

            if (Globals.PathOfWorkspace.ToLower() == item.DirectoryPath.ToLower())
            {
                return;
            }

            ChangeWorkspace(item.DirectoryPath);
        }

        #region 最小化到托盘图标

        WindowState wsl;
        System.Windows.Forms.NotifyIcon notifyIcon;

        private void CreateNoticeIcon()
        {
            this.notifyIcon = new System.Windows.Forms.NotifyIcon();
            this.notifyIcon.BalloonTipText = Globals.AppName; //设置程序启动时显示的文本
            this.notifyIcon.Text = Globals.AppName;//最小化到托盘时，鼠标点击时显示的文本
            this.notifyIcon.Icon = new System.Drawing.Icon("App.ico");//程序图标
            this.notifyIcon.Visible = true;
            notifyIcon.MouseDoubleClick += OnNotifyIconDoubleClick;
            notifyIcon.MouseClick += NotifyIcon_MouseClick;
            //this.notifyIcon.ShowBalloonTip(1000);
        }

        private ContextMenu iconContextMenu = null;

        private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (iconContextMenu == null)
            {
                iconContextMenu = new ContextMenu();
                MenuItem miShowWindow = new MenuItem() { Header = "显示窗口(_S)" };
                miShowWindow.Click += MiShowWindow_Click;

                MenuItem miExit = new MenuItem() { Header = "退出(_X)" };
                miExit.Click += MiExit_Click;

                this.iconContextMenu.Items.Add(miShowWindow);
                this.iconContextMenu.Items.Add(miExit);
            }

            iconContextMenu.IsOpen = true;
        }

        private void MiExit_Click(object sender, RoutedEventArgs e)
        {
            //App.Current.Shutdown();//注意，这能使用这个
            this.isForceExit = true;//强行忽略这个字段的值。
            this.Close();
        }

        private void MiShowWindow_Click(object sender, RoutedEventArgs e)
        {
            this.Show();
            WindowState = wsl;
        }

        private void OnNotifyIconDoubleClick(object sender, EventArgs e)
        {
            this.Show();
            WindowState = wsl;
        }

        private bool isCloseToIcon = false;
        /// <summary>
        /// 关闭到托盘图标。
        /// </summary>
        public bool IsCloseToIcon
        {
            get
            {
                return isCloseToIcon;
            }
        }

        /// <summary>
        /// 是否在编译工作区时忽略被加密的文件，否则每次都会要求输入密码。
        /// </summary>
        public bool IgnoreEncryptedFiles { get; private set; }

        /// <summary>
        /// 忽略isCloseToIcon的值，强制退出。
        /// </summary>
        private bool isForceExit = false;

        private void miCloseToIcon_Click(object sender, RoutedEventArgs e)
        {
            isCloseToIcon = miCloseToIcon.IsChecked = !miCloseToIcon.IsChecked;

            this.ConfigManager.Set("CloseToIcon", isCloseToIcon.ToString());
        }
        #endregion

        private void miSearchAnchorsInDirectory_Click(object sender, RoutedEventArgs e)
        {
            var wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;

            if (wtvi == null)
            {
                LMessageBox.Show("　　请先在工作区管理器中选择一个目录作为查找目标！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (wtvi.IsDirectoryExists == false)
            {
                LMessageBox.Show("　　请选择目录而不是其它项目！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (wtvi.FullPath.EndsWith("~") || wtvi.FullPath.EndsWith("~\\"))
            {
                LMessageBox.Show("　　以波型符结尾的目录是资源目录，不允许执行此操作！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            //锚的定义方式：
            //[锚名](@锚ID)
            //锚名可以省略，但锚ID不能省略。
            //编译后，会变成这样：
            //<a id="锚ID">锚名</a>
            FindTextInAllFiles(wtvi.FullPath, "](@", '[', ')');

            if (rightToolBar.SelectedIndex != 1)
            {
                rightToolBar.SelectedIndex = 1;
            }

            if (cdRightToolsArea.ActualWidth < 140)
            {
                cdLeftToolsArea.Width = new GridLength(140);
            }

        }

        private void FindTodolistInDirectory(string headerKey)
        {
            tvTaskList.Items.Clear();

            var wtvi = tvWorkDirectory.SelectedItem as WorkspaceTreeViewItem;

            if (wtvi == null)
            {
                LMessageBox.Show("　　请先在工作区管理器中选择一个目录作为查找目标！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (wtvi.IsDirectoryExists == false)
            {
                LMessageBox.Show("　　请选择目录而不是其它项目！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (wtvi.FullPath.EndsWith("~") || wtvi.FullPath.EndsWith("~\\"))
            {
                LMessageBox.Show("　　以波型符结尾的目录是资源目录，不允许执行此操作！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            FindTodoListInAllFiles(wtvi.FullPath, headerKey);

            if (tvTaskList.Items.Count <= 0)
            {
                tvTaskList.Items.Add("<没找到结果...>");
            }

            if (rightToolBar.SelectedIndex != 3)
            {
                rightToolBar.SelectedIndex = 3;
            }

            if (cdRightToolsArea.ActualWidth < 140)
            {
                cdLeftToolsArea.Width = new GridLength(140);
            }
        }

        private void miSearchUnStartTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("[-]");
        }

        private void miSearchUnFinishedTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("![+]");
        }

        private void miSearchProcessingTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("[%]");
        }

        private void miSearchAbortedTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("[#]");
        }

        private void miSearchFinishedTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("[+]");
        }

        private void miSearchAllTodolistItemInDirectory_Click(object sender, RoutedEventArgs e)
        {
            FindTodolistInDirectory("");
        }

        private void miSearchUnStartTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("[-]");
        }

        private void miSearchProcessingTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("[%]");
        }

        private void miSearchAbortedTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("[#]");
        }

        private void miSearchFinishedTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("[+]");
        }

        private void miSearchAllTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("");
        }

        private void btnFindTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("");
        }

        private void miSearchUnFinishedTodolistItem_Click(object sender, RoutedEventArgs e)
        {
            FindTodoList("![+]");
        }

        private void miFindInActiveDocument_Click(object sender, RoutedEventArgs e)
        {
            if (gdFindAndReplace.Visibility != Visibility.Visible)
            {
                gdFindAndReplace.Visibility = Visibility.Visible;
                cmbFindText.UpdateLayout();
            }
            var editor = ActivedEditor;
            if (editor != null)
            {
                cmbFindText.Text = editor.EditorBase.SelectedText;
            }
            else return;

            cmbSearchArea.SelectedIndex = 0;

            cmbFindText.Focus();
            if (cmbFindText.Text.Length > 0)
            {
                btnFind_Click(sender, e);
            }

            //独立窗口效果不好，整合到主界面上。
            //var activeEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
            //if (activeEditor != null)
            //{
            //    FindReplaceDialog.ShowForFind(activeEditor.EditorBase, this);
            //}
        }

        private void miFindAndReplaceInActiveDocument_Click(object sender, RoutedEventArgs e)
        {
            //打开查找替换面板
            if (gdFindAndReplace.Visibility != Visibility.Visible)
            {
                gdFindAndReplace.Visibility = Visibility.Visible;
                cmbFindText.UpdateLayout();
            }
            var editor = ActivedEditor;
            if (editor != null)
            {
                cmbFindText.Text = editor.EditorBase.SelectedText;
            }
            else return;

            cmbSearchArea.SelectedIndex = 0;

            if (cmbFindText.Text.Length > 0)
            {
                cmbReplaceTextInputBox.Focus();
            }

            //var activeEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
            //if (activeEditor != null)
            //{
            //    FindReplaceDialog.ShowForReplace(activeEditor.EditorBase, this);
            //}
        }

        private void miInsertDate_Click(object sender, RoutedEventArgs e)
        {
            InsertDateText();
        }

        public static string FormatDateText(DateTime datetime)
        {
            var srcText = datetime.Date.ToShortDateString();
            var spans = srcText.Split(new char[] { '-', '.', ',', '，', '。', '－', '、', '．', '/' }, StringSplitOptions.RemoveEmptyEntries);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < spans.Length; i++)
            {
                var span = spans[i];
                if (span.Length == 1)
                {
                    span = '0' + span;
                }
                sb.Append(span + "-");
            }

            var result = sb.ToString();
            if (result.EndsWith("-")) return result.Substring(0, result.Length - 1);

            return result;
        }

        public static string FormatDateTimeText(DateTime datetime)
        {
            var srcText = datetime.Date.ToShortDateString();
            var spans = srcText.Split(new char[] { '-', '.', ',', '，', '。', '－', '、', '．', '/' }, StringSplitOptions.RemoveEmptyEntries);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < spans.Length; i++)
            {
                var span = spans[i];
                if (span.Length == 1)
                {
                    span = '0' + span;
                }
                sb.Append(span + "-");
            }

            var result = sb.ToString();
            if (result.EndsWith("-")) result = result.Substring(0, result.Length - 1);

            var timeText = datetime.ToShortTimeString();
            StringBuilder sb2 = new StringBuilder();
            var spans2 = timeText.Split(new char[] { ':' });
            for (int i = 0; i < spans2.Length; i++)
            {
                var span = spans2[i];
                if (span.Length == 1)
                {
                    span = '0' + span;
                }
                sb2.Append(span + ":");
            }
            var result2 = sb2.ToString();
            result += " " + result2.Substring(0, result2.Length - 1);

            return result;
        }

        private void InsertDateText()
        {
            var activeEditor = this.mainTabControl.SelectedItem as MarkdownEditor;
            if (activeEditor == null) return;

            var now = DateTime.Now;
            var header = "";
            if (activeEditor.EditorBase.Document.GetLineByOffset(activeEditor.EditorBase.SelectionStart).Offset == activeEditor.EditorBase.SelectionStart)
            {
                header = " ";//自动格式化一下
            }

            var line = activeEditor.EditorBase.Document.GetLineByOffset(activeEditor.EditorBase.SelectionStart);
            var lineText = activeEditor.EditorBase.Document.GetText(line.Offset, line.Length);
            if (CustomMarkdownSupport.IsTodoLine(lineText))
            {
                //在最后插入一个新行
                DocumentLine lastDateLine = null;
                for (int i = line.LineNumber + 1; i <= activeEditor.EditorBase.Document.LineCount; i++)//LineNumber从１开始。
                {
                    var l = activeEditor.EditorBase.Document.GetLineByNumber(i);
                    var lt = activeEditor.EditorBase.Document.GetText(l.Offset, l.Length);

                    if (CustomMarkdownSupport.IsDateLine(lt))
                    {
                        lastDateLine = l;
                    }
                    else if (CustomMarkdownSupport.IsTodoLine(lt) || lt.StartsWith("#"))
                    {
                        if (i - 2 > line.LineNumber)
                        {
                            lastDateLine = activeEditor.EditorBase.Document.GetLineByNumber(i - 2);
                        }
                        else if (i - 1 > line.LineNumber)
                        {
                            lastDateLine = activeEditor.EditorBase.Document.GetLineByNumber(i - 2);
                        }
                        break;
                    }
                }

                if (lastDateLine == null) lastDateLine = line;//如果找不到，就直接在当前行后面添加。

                var markText = CustomMarkdownSupport.GetHeaderOfTodolistItem(lineText);
                if (markText.Contains("-")) markText = "[-]";
                else if (markText.Contains("%")) markText = "[%]";
                else if (markText.Contains("#")) markText = "[#]";
                else if (markText.Contains("+")) markText = "[+]";
                else markText = "";

                header = " ";

                var newDateLine = $"{header}[{FormatDateText(now)} {now.ToShortTimeString()}]{markText} ";
                activeEditor.EditorBase.Document.Insert(lastDateLine.EndOffset, $"\r\n\r\n{newDateLine}");
                activeEditor.EditorBase.Select(lastDateLine.EndOffset + 4 + newDateLine.Length, 0);
            }
            else if (CustomMarkdownSupport.IsDateLine(lineText))
            {
                //修改当前行的时间
                var indexStart = lineText.IndexOf("[");
                var indexEnd = lineText.IndexOf("]");
                if (indexStart >= 0 && indexEnd >= 0)
                {
                    //修改当前时间
                    var newLineText = lineText.Substring(0, indexStart + 1) + FormatDateText(DateTime.Now) +
                       $" {now.ToShortTimeString()}" + lineText.Substring(indexEnd);
                    activeEditor.EditorBase.Document.Replace(line.Offset, line.Length, newLineText);
                }
                else
                {
                    //在当前位置插入时间
                    activeEditor.EditorBase.SelectedText = $"{header}[{FormatDateText(now)} {now.ToShortTimeString()}] ";
                    activeEditor.EditorBase.Select(activeEditor.EditorBase.SelectionStart +
                        activeEditor.EditorBase.SelectionLength, 0);
                }
            }
            else
            {
                //在当前位置插入时间

                activeEditor.EditorBase.SelectedText = $"{header}[{FormatDateText(now)} {now.ToShortTimeString()}] ";
                activeEditor.EditorBase.Select(activeEditor.EditorBase.SelectionStart +
                    activeEditor.EditorBase.SelectionLength, 0);
            }
        }

        private void miIgnoreEncryptedFile_Click(object sender, RoutedEventArgs e)
        {
            this.IgnoreEncryptedFiles =
            miIgnoreEncryptedFile.IsChecked = !miIgnoreEncryptedFile.IsChecked;
            this.ConfigManager.Set("IgnoreEncryptedFiles", miIgnoreEncryptedFile.IsChecked.ToString());
        }

        private void miHideChoiceQuestionAnswer_Click(object sender, RoutedEventArgs e)
        {
            this.hideChoiceQuestionAnswer =
            miHideChoiceQuestionAnswer.IsChecked = !miHideChoiceQuestionAnswer.IsChecked;
            this.ConfigManager.Set("HideChoiceQuestionAnswer", miHideChoiceQuestionAnswer.IsChecked.ToString());
        }

        private void btnCloseFindAndReplacePanel_Click(object sender, RoutedEventArgs e)
        {
            gdFindAndReplace.Visibility = Visibility.Collapsed;
        }

        private void btnFind_Click(object sender, RoutedEventArgs e)
        {
            Find();
        }

        public void Find()
        {
            if (!FindNext(cmbFindText.Text))
                SystemSounds.Beep.Play();

            cmbFindText.Items.Insert(0, cmbFindText.Text);
        }

        private Regex GetRegEx(string textToFind, bool leftToRight = false)
        {
            RegexOptions options = RegexOptions.None;
            if (cbSearchUp.IsChecked == true && !leftToRight)
                options |= RegexOptions.RightToLeft;
            if (cbCaseSensitive.IsChecked == false)
                options |= RegexOptions.IgnoreCase;

            options |= RegexOptions.Multiline;

            if (cbRegex.IsChecked == true)
            {
                return new Regex(textToFind, options);
            }
            else
            {
                string pattern = Regex.Escape(textToFind);
                if (cbWildcards.IsChecked == true)
                    pattern = pattern.Replace("\\*", ".*").Replace("\\?", ".");
                if (cbWholeWord.IsChecked == true)
                    pattern = "\\b" + pattern + "\\b";
                return new Regex(pattern, options);
            }
        }

        private bool FindNext(string textToFind)
        {
            var activeEditor = ActivedEditor;
            if (activeEditor == null) return false;

            Regex regex = GetRegEx(textToFind);
            int start = regex.Options.HasFlag(RegexOptions.RightToLeft) ?
            activeEditor.EditorBase.SelectionStart : activeEditor.EditorBase.SelectionStart + activeEditor.EditorBase.SelectionLength;
            Match match = regex.Match(activeEditor.EditorBase.Text, start);

            if (!match.Success)  // start again from beginning or end
            {
                if (regex.Options.HasFlag(RegexOptions.RightToLeft))
                    match = regex.Match(activeEditor.EditorBase.Text, activeEditor.EditorBase.Text.Length);
                else
                    match = regex.Match(activeEditor.EditorBase.Text, 0);
            }

            if (match.Success)
            {
                activeEditor.EditorBase.Select(match.Index, match.Length);
                TextLocation loc = activeEditor.EditorBase.Document.GetLocation(match.Index);
                activeEditor.EditorBase.ScrollTo(loc.Line, loc.Column);
            }

            return match.Success;
        }

        private void btnReplace_Click(object sender, RoutedEventArgs e)
        {
            var activeEditor = ActivedEditor;
            if (activeEditor == null) return;

            Regex regex = GetRegEx(cmbFindText.Text);
            string input = activeEditor.EditorBase.Text.Substring(activeEditor.EditorBase.SelectionStart, activeEditor.EditorBase.SelectionLength);
            Match match = regex.Match(input);
            bool replaced = false;
            if (match.Success && match.Index == 0 && match.Length == input.Length)
            {
                activeEditor.EditorBase.Document.Replace(activeEditor.EditorBase.SelectionStart, activeEditor.EditorBase.SelectionLength, cmbReplaceTextInputBox.Text);
                replaced = true;
            }

            if (!FindNext(cmbFindText.Text) && !replaced)
                SystemSounds.Beep.Play();

            cmbFindText.Items.Insert(0, cmbFindText.Text);
            cmbReplaceTextInputBox.Items.Insert(0, cmbReplaceTextInputBox.Text);
        }

        private void btnReplaceAll_Click(object sender, RoutedEventArgs e)
        {
            var activeEditor = ActivedEditor;
            if (activeEditor == null) return;

            var result = LMessageBox.Show("你确定把所有找到的 “" +
                                cmbFindText.Text + "” 替换成 “" + cmbReplaceTextInputBox.Text + "”吗？",
                                "Lunar Markdown Edit - 全部替换", MessageBoxButton.OKCancel, MessageBoxImage.Question);
            if (result == MessageBoxResult.OK)
            {
                Regex regex = GetRegEx(cmbFindText.Text, true);
                int offset = 0;
                activeEditor.EditorBase.BeginChange();
                foreach (Match match in regex.Matches(activeEditor.EditorBase.Text))
                {
                    activeEditor.EditorBase.Document.Replace(offset + match.Index, match.Length, cmbReplaceTextInputBox.Text);
                    offset += cmbReplaceTextInputBox.Text.Length - match.Length;
                }
                activeEditor.EditorBase.EndChange();

                cmbFindText.Items.Insert(0, cmbFindText.Text);
                cmbReplaceTextInputBox.Items.Insert(0, cmbReplaceTextInputBox.Text);
            }
        }

        private void cmbSearchArea_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            RefreshFindButtonsStatus();
        }

        private void RefreshFindButtonsStatus()
        {
            if (btnFind == null) return;
            if (btnReplace == null) return;
            if (btnReplaceAll == null) return;
            if (cmbReplaceTextInputBox == null) return;
            if (cbCaseSensitive == null) return;
            if (cbWholeWord == null) return;
            if (cbRegex == null) return;
            if (cbWildcards == null) return;
            if (cbSearchUp == null) return;

            switch (cmbSearchArea.SelectedIndex)
            {
                case 0:
                    {
                        btnFind.IsEnabled =
                            btnReplace.IsEnabled =
                            btnReplaceAll.IsEnabled =
                            cmbReplaceTextInputBox.IsEnabled =
                            cbCaseSensitive.IsEnabled =
                            cbRegex.IsEnabled =
                            cbSearchUp.IsEnabled =
                            cbWholeWord.IsEnabled =
                            cbWildcards.IsEnabled = true;
                        break;
                    }
                default:
                    {
                        btnFind.IsEnabled =
                            btnReplace.IsEnabled =
                            btnReplaceAll.IsEnabled =
                            cmbReplaceTextInputBox.IsEnabled =
                            cbCaseSensitive.IsEnabled =
                            cbRegex.IsEnabled =
                            cbSearchUp.IsEnabled =
                            cbWholeWord.IsEnabled =
                            cbWildcards.IsEnabled = false;
                        break;
                    }
            }
        }

        private void btnFindTodoListItems_AppendArrow_Initialized(object sender, EventArgs e)
        {
            //SplitterButton的弹出菜单要处理一下，左键单显示，右键不显示
            splitMenu.PlacementTarget = btnFindTodoListItems_AppendArrow;
            btnFindTodoListItems_AppendArrow.ContextMenu = null;
        }

        private void btnFindTodoListItems_AppendArrow_Click(object sender, RoutedEventArgs e)
        {
            splitMenu.HorizontalOffset = -btnFindTodoListItems.ActualWidth;
            splitMenu.PlacementTarget = btnFindTodoListItems_AppendArrow;
            splitMenu.Placement = PlacementMode.Bottom;
            splitMenu.IsOpen = true;
        }
    }
}
